Documentation
¶
Overview ¶
Package graph provides code relationship graph types and operations.
The graph package contains types for representing code as a directed graph where nodes are symbols (functions, types, variables) and edges represent relationships (calls, imports, implements, etc.).
Ownership Model ¶
The graph stores pointers to symbols but does NOT own them:
- Symbols MUST NOT be mutated after being added via AddNode()
- The graph does NOT copy symbols (for memory efficiency)
- Same ownership contract as the index package
Thread Safety ¶
Graph is NOT safe for concurrent use during building. It is designed for:
- Single-writer access during build phase (AddNode, AddEdge calls)
- Read-only access after Freeze() is called
After Freeze(), the graph can be safely read from multiple goroutines.
Lifecycle ¶
A typical graph lifecycle:
- Create with NewGraph(projectRoot)
- Build with AddNode() and AddEdge() calls
- Call Freeze() to finalize
- Query with GetNode(), traversal methods, etc.
Index ¶
- Constants
- Variables
- func GetCorrelationID(ctx context.Context) string
- func LevelName(level int) string
- func NullLogger() *slog.Logger
- func ProjectHash(projectRoot string) string
- func RecordFileMtimes(g *Graph, projectRoot string)
- func ShouldDoIncrementalUpdate(changedCount, totalCount int) bool
- type AggregateFunc
- type ArticulationResult
- type BuildProgress
- type BuildResult
- type BuildStats
- type Builder
- type BuilderOption
- func WithBuilderMaxEdges(n int) BuilderOption
- func WithBuilderMaxNodes(n int) BuilderOption
- func WithLSPEnrichment(config *LSPEnrichmentConfig) BuilderOption
- func WithMaxMemoryMB(mb int) BuilderOption
- func WithProgressCallback(fn ProgressFunc) BuilderOption
- func WithProjectRoot(root string) BuilderOption
- func WithWorkerCount(n int) BuilderOption
- type BuilderOptions
- type CRSGraphAdapter
- func (a *CRSGraphAdapter) Analytics() crs.GraphAnalyticsQuery
- func (a *CRSGraphAdapter) CallEdgeCount(ctx context.Context) (int, error)
- func (a *CRSGraphAdapter) Close() error
- func (a *CRSGraphAdapter) EdgeCount() int
- func (a *CRSGraphAdapter) FindCallees(ctx context.Context, symbolID string) ([]*ast.Symbol, error)
- func (a *CRSGraphAdapter) FindCallers(ctx context.Context, symbolID string) ([]*ast.Symbol, error)
- func (a *CRSGraphAdapter) FindImplementations(ctx context.Context, interfaceName string) ([]*ast.Symbol, error)
- func (a *CRSGraphAdapter) FindReferences(ctx context.Context, symbolID string) ([]*ast.Symbol, error)
- func (a *CRSGraphAdapter) FindSymbolByID(ctx context.Context, id string) (*ast.Symbol, bool, error)
- func (a *CRSGraphAdapter) FindSymbolsByKind(ctx context.Context, kind ast.SymbolKind) ([]*ast.Symbol, error)
- func (a *CRSGraphAdapter) FindSymbolsByName(ctx context.Context, name string) ([]*ast.Symbol, error)
- func (a *CRSGraphAdapter) FindSymbolsInFile(ctx context.Context, filePath string) ([]*ast.Symbol, error)
- func (a *CRSGraphAdapter) Generation() int64
- func (a *CRSGraphAdapter) GetCallChain(ctx context.Context, fromID, toID string, maxDepth int) ([]string, error)
- func (a *CRSGraphAdapter) GetEdgeCountByFile(ctx context.Context, filePath string) (int, error)
- func (a *CRSGraphAdapter) GetEdgesByFile(ctx context.Context, filePath string) ([]*Edge, error)
- func (a *CRSGraphAdapter) HasCycleFrom(ctx context.Context, symbolID string) (bool, error)
- func (a *CRSGraphAdapter) InvalidateCache()
- func (a *CRSGraphAdapter) LastRefreshTime() int64
- func (a *CRSGraphAdapter) NodeCount() int
- func (a *CRSGraphAdapter) QueryCacheStats() QueryCacheStats
- func (a *CRSGraphAdapter) ShortestPath(ctx context.Context, fromID, toID string) ([]string, error)
- type CacheStats
- type CircuitBreaker
- type CircuitBreakerStats
- type CircuitState
- type Community
- type CommunityResult
- type ControlDependence
- type ControlDependenceError
- type CouplingMetrics
- type CyclicDependency
- type DeadCodeNode
- type DiffSummary
- type DirtyEntry
- type DirtyTracker
- func (d *DirtyTracker) Clear(paths []string) int
- func (d *DirtyTracker) ClearAll() int
- func (d *DirtyTracker) Count() int
- func (d *DirtyTracker) Disable()
- func (d *DirtyTracker) Enable()
- func (d *DirtyTracker) GetDirtyEntries() []DirtyEntry
- func (d *DirtyTracker) GetDirtyFiles() []string
- func (d *DirtyTracker) HasDirty() bool
- func (d *DirtyTracker) IsEnabled() bool
- func (d *DirtyTracker) MarkDirty(path string)
- func (d *DirtyTracker) MarkDirtyFromWatcher(change FileChange)
- func (d *DirtyTracker) MarkDirtyWithSource(path, source string)
- type DiskGraph
- func (dg *DiskGraph) BuiltAtMilli() int64
- func (dg *DiskGraph) Close() error
- func (dg *DiskGraph) EdgeCount() int
- func (dg *DiskGraph) FileMtimes() map[string]int64
- func (dg *DiskGraph) GetNode(id string) (*Node, bool)
- func (dg *DiskGraph) GraphHash() string
- func (dg *DiskGraph) LoadAsGraph(ctx context.Context) (*Graph, error)
- func (dg *DiskGraph) NodeCount() int
- func (dg *DiskGraph) ProjectRoot() string
- type DominanceFrontier
- type DominanceFrontierError
- type DominatorError
- type DominatorTree
- func (dt *DominatorTree) DominatedBy(node string) []string
- func (dt *DominatorTree) Dominates(a, b string) bool
- func (dt *DominatorTree) DominatorsOf(node string) []string
- func (dt *DominatorTree) LowestCommonDominator(a, b string) string
- func (dt *DominatorTree) LowestCommonDominatorMultiple(nodes []string) string
- func (dt *DominatorTree) MaxDepth() int
- func (dt *DominatorTree) PrepareLCAQueries() *LCAQueryEngine
- type DominatorTreeCache
- type Edge
- type EdgeError
- type EdgeType
- type EnrichmentStats
- type ExternalDependency
- type FileChange
- type FileChangeHandler
- type FileClassification
- type FileClassificationOptions
- type FileClassificationStats
- type FileError
- type FileOp
- type FileParseError
- type FileWatcher
- type FileWatcherOptions
- type Graph
- func (g *Graph) AddEdge(fromID, toID string, edgeType EdgeType, loc ast.Location) error
- func (g *Graph) AddNode(symbol *ast.Symbol) (*Node, error)
- func (g *Graph) Clone() *Graph
- func (g *Graph) EdgeCount() int
- func (g *Graph) Edges() []*Edge
- func (g *Graph) FindCalleesByID(ctx context.Context, symbolID string, opts ...QueryOption) (*QueryResult, error)
- func (g *Graph) FindCalleesByName(ctx context.Context, name string, opts ...QueryOption) (map[string]*QueryResult, error)
- func (g *Graph) FindCallersByID(ctx context.Context, symbolID string, opts ...QueryOption) (*QueryResult, error)
- func (g *Graph) FindCallersByName(ctx context.Context, name string, opts ...QueryOption) (map[string]*QueryResult, error)
- func (g *Graph) FindCallersWithInheritance(ctx context.Context, symbolID string, parentMethodIDs []string, ...) (*InheritanceQueryResult, error)
- func (g *Graph) FindImplementationsByID(ctx context.Context, interfaceID string, opts ...QueryOption) (*QueryResult, error)
- func (g *Graph) FindImplementationsByName(ctx context.Context, name string, opts ...QueryOption) (map[string]*QueryResult, error)
- func (g *Graph) FindImporters(ctx context.Context, packagePath string, opts ...QueryOption) ([]string, error)
- func (g *Graph) FindReferencesByID(ctx context.Context, symbolID string, opts ...QueryOption) ([]ast.Location, error)
- func (g *Graph) Freeze()
- func (g *Graph) GetCallGraph(ctx context.Context, symbolID string, opts ...QueryOption) (*TraversalResult, error)
- func (g *Graph) GetCallGraphParallel(ctx context.Context, symbolID string, opts ...QueryOption) (*TraversalResult, error)
- func (g *Graph) GetDependencyTree(ctx context.Context, filePath string, opts ...QueryOption) (*TraversalResult, error)
- func (g *Graph) GetEdgeCountByFile(filePath string) int
- func (g *Graph) GetEdgeCountByType(edgeType EdgeType) int
- func (g *Graph) GetEdgesByFile(filePath string) []*Edge
- func (g *Graph) GetEdgesByType(edgeType EdgeType) []*Edge
- func (g *Graph) GetNode(id string) (*Node, bool)
- func (g *Graph) GetNodeCountByKind(kind ast.SymbolKind) int
- func (g *Graph) GetNodeCountByName(name string) int
- func (g *Graph) GetNodesByFile(filePath string) []*Node
- func (g *Graph) GetNodesByKind(kind ast.SymbolKind) []*Node
- func (g *Graph) GetNodesByName(name string) []*Node
- func (g *Graph) GetReverseCallGraph(ctx context.Context, symbolID string, opts ...QueryOption) (*TraversalResult, error)
- func (g *Graph) GetReverseCallGraphParallel(ctx context.Context, symbolID string, opts ...QueryOption) (*TraversalResult, error)
- func (g *Graph) GetTypeHierarchy(ctx context.Context, typeID string, opts ...QueryOption) (*TraversalResult, error)
- func (g *Graph) Hash() string
- func (g *Graph) IsFrozen() bool
- func (g *Graph) IsTree(ctx context.Context, root string) error
- func (g *Graph) MaterializeToDisk(ctx context.Context, path string) error
- func (g *Graph) MergeParseResult(result *ast.ParseResult) (int, error)
- func (g *Graph) NodeCount() int
- func (g *Graph) Nodes() func(yield func(string, *Node) bool)
- func (g *Graph) RemoveFile(filePath string) (int, error)
- func (g *Graph) RemoveNode(id string) error
- func (g *Graph) ReplaceEdgeTarget(edge *Edge, newToID string) error
- func (g *Graph) ShortestPath(ctx context.Context, fromID, toID string) (*PathResult, error)
- func (g *Graph) State() GraphState
- func (g *Graph) Stats() GraphStats
- func (g *Graph) ToSerializable() *SerializableGraph
- func (g *Graph) Validate() error
- type GraphAnalytics
- func (a *GraphAnalytics) ArticulationPoints(ctx context.Context) (*ArticulationResult, error)
- func (a *GraphAnalytics) ArticulationPointsWithCRS(ctx context.Context) (*ArticulationResult, crs.TraceStep)
- func (ga *GraphAnalytics) BatchLCAWithCRS(ctx context.Context, pairs [][2]string) ([]string, []error, error)
- func (a *GraphAnalytics) CheckReducibility(ctx context.Context, domTree *DominatorTree) (*ReducibilityResult, error)
- func (a *GraphAnalytics) CheckReducibilityWithCRS(ctx context.Context, domTree *DominatorTree) (*ReducibilityResult, crs.TraceStep)
- func (a *GraphAnalytics) ComputeControlDependence(ctx context.Context, postDomTree *DominatorTree) (*ControlDependence, error)
- func (a *GraphAnalytics) ComputeControlDependenceWithCRS(ctx context.Context, postDomTree *DominatorTree) (*ControlDependence, crs.TraceStep)
- func (a *GraphAnalytics) ComputeDominanceFrontier(ctx context.Context, domTree *DominatorTree) (*DominanceFrontier, error)
- func (a *GraphAnalytics) ComputeDominanceFrontierWithCRS(ctx context.Context, domTree *DominatorTree) (*DominanceFrontier, crs.TraceStep)
- func (a *GraphAnalytics) CyclicDependencies() []CyclicDependency
- func (a *GraphAnalytics) CyclicDependenciesWithCRS(ctx context.Context) ([]CyclicDependency, crs.TraceStep)
- func (a *GraphAnalytics) DeadCode() []DeadCodeNode
- func (a *GraphAnalytics) DeadCodeWithCRS(ctx context.Context) ([]DeadCodeNode, crs.TraceStep)
- func (ga *GraphAnalytics) DecomposePathWithCRS(ctx context.Context, u, v string) ([]PathSegment, error)
- func (a *GraphAnalytics) DetectCommunities(ctx context.Context, opts *LeidenOptions) (*CommunityResult, error)
- func (a *GraphAnalytics) DetectCommunitiesParallel(ctx context.Context, opts *LeidenOptions) (*CommunityResult, error)
- func (a *GraphAnalytics) DetectCommunitiesWithCRS(ctx context.Context, opts *LeidenOptions) (*CommunityResult, crs.TraceStep)
- func (a *GraphAnalytics) DetectEntryNodes(ctx context.Context) []string
- func (a *GraphAnalytics) DetectLoops(ctx context.Context, domTree *DominatorTree) (*LoopNest, error)
- func (a *GraphAnalytics) DetectLoopsWithCRS(ctx context.Context, domTree *DominatorTree) (*LoopNest, crs.TraceStep)
- func (a *GraphAnalytics) DetectSESERegions(ctx context.Context, domTree, postDomTree *DominatorTree) (*SESEResult, error)
- func (a *GraphAnalytics) DetectSESERegionsWithCRS(ctx context.Context, domTree, postDomTree *DominatorTree) (*SESEResult, crs.TraceStep)
- func (ga *GraphAnalytics) DistanceWithCRS(ctx context.Context, u, v string) (int, error)
- func (a *GraphAnalytics) Dominators(ctx context.Context, entry string) (*DominatorTree, error)
- func (a *GraphAnalytics) DominatorsWithCRS(ctx context.Context, entry string) (*DominatorTree, crs.TraceStep)
- func (a *GraphAnalytics) DominatorsWithCache(ctx context.Context, entry string) (*DominatorTree, crs.TraceStep, error)
- func (ga *GraphAnalytics) EndSession(ctx context.Context) error
- func (a *GraphAnalytics) GetCouplingForPackage(pkg string) *CouplingMetrics
- func (a *GraphAnalytics) GetNode(id string) (*Node, bool)
- func (a *GraphAnalytics) HotSpots(top int) []HotspotNode
- func (a *GraphAnalytics) HotSpotsWithCRS(ctx context.Context, top int) ([]HotspotNode, crs.TraceStep)
- func (a *GraphAnalytics) IsGraphReady() bool
- func (a *GraphAnalytics) IsProductionFile(filePath string) bool
- func (a *GraphAnalytics) IsReducible(ctx context.Context, domTree *DominatorTree) (bool, error)
- func (ga *GraphAnalytics) LCAWithCRS(ctx context.Context, u, v string) (string, error)
- func (a *GraphAnalytics) LCDWithCRS(ctx context.Context, domTree *DominatorTree, nodes []string) (string, crs.TraceStep)
- func (a *GraphAnalytics) PackageCoupling() map[string]CouplingMetrics
- func (a *GraphAnalytics) PackageCouplingWithCRS(ctx context.Context) (map[string]CouplingMetrics, crs.TraceStep)
- func (a *GraphAnalytics) PageRank(ctx context.Context, opts *PageRankOptions) *PageRankResult
- func (a *GraphAnalytics) PageRankTop(ctx context.Context, k int, opts *PageRankOptions) []PageRankNode
- func (a *GraphAnalytics) PageRankTopWithCRS(ctx context.Context, k int, opts *PageRankOptions) ([]PageRankNode, crs.TraceStep)
- func (a *GraphAnalytics) PageRankWithCRS(ctx context.Context, opts *PageRankOptions) (*PageRankResult, crs.TraceStep)
- func (ga *GraphAnalytics) PathGCDWithCRS(ctx context.Context, u, v string, valueFunc func(string) int64) (int64, error)
- func (ga *GraphAnalytics) PathMaxWithCRS(ctx context.Context, u, v string, valueFunc func(string) int64) (int64, error)
- func (ga *GraphAnalytics) PathMinWithCRS(ctx context.Context, u, v string, valueFunc func(string) int64) (int64, error)
- func (ga *GraphAnalytics) PathSumWithCRS(ctx context.Context, u, v string, valueFunc func(string) int64) (int64, error)
- func (ga *GraphAnalytics) PopSession(ctx context.Context) error
- func (a *GraphAnalytics) PostDominators(ctx context.Context, exit string) (*DominatorTree, error)
- func (a *GraphAnalytics) PostDominatorsWithCRS(ctx context.Context, exit string) (*DominatorTree, crs.TraceStep)
- func (a *GraphAnalytics) PostDominatorsWithCache(ctx context.Context, exit string) (*DominatorTree, crs.TraceStep, error)
- func (ga *GraphAnalytics) PushSession(ctx context.Context, sessionID string) error
- func (a *GraphAnalytics) ShortestPath(ctx context.Context, fromID, toID string) (*PathResult, error)
- func (ga *GraphAnalytics) StartSession(ctx context.Context, sessionID string) error
- type GraphAnalyticsOptions
- type GraphHolder
- type GraphOption
- type GraphOptions
- type GraphState
- type GraphStats
- type HLDCache
- type HLDForest
- func (f *HLDForest) GetHLD(nodeID string) *HLDecomposition
- func (f *HLDForest) GetHLDByIndex(index int) *HLDecomposition
- func (f *HLDForest) GetRoot(index int) string
- func (f *HLDForest) GetTreeID(nodeID string) (int, error)
- func (f *HLDForest) GetTreeOffset(nodeID string) (int, error)
- func (f *HLDForest) GraphHash() string
- func (f *HLDForest) TotalNodes() int
- func (f *HLDForest) TreeCount() int
- func (f *HLDForest) Validate() error
- type HLDStats
- type HLDecomposition
- func (hld *HLDecomposition) BatchDecomposePath(ctx context.Context, pairs [][2]string) ([][]PathSegment, []error, error)
- func (hld *HLDecomposition) BatchDistance(ctx context.Context, pairs [][2]string) ([]int, []error, error)
- func (hld *HLDecomposition) BatchLCA(ctx context.Context, pairs [][2]string) ([]string, []error, error)
- func (hld *HLDecomposition) CacheKey() string
- func (hld *HLDecomposition) CacheKeyWithGraphHash(graphHash string) string
- func (hld *HLDecomposition) DecomposePath(ctx context.Context, u, v string) (segments []PathSegment, err error)
- func (hld *HLDecomposition) Depth(v int) int
- func (hld *HLDecomposition) Distance(ctx context.Context, u, v string) (int, error)
- func (hld *HLDecomposition) EstimateDistanceCost(u, v string) (int, error)
- func (hld *HLDecomposition) EstimateLCACost(u, v string) (int, error)
- func (hld *HLDecomposition) EstimatePathCost(u, v string) (int, error)
- func (hld *HLDecomposition) GetLCAStats() LCAStats
- func (hld *HLDecomposition) GraphHash() string
- func (hld *HLDecomposition) Head(v int) int
- func (hld *HLDecomposition) HeavyChild(v int) int
- func (hld *HLDecomposition) IdxToNode(idx int) (string, error)
- func (hld *HLDecomposition) InitStats()
- func (hld *HLDecomposition) IsAncestor(ctx context.Context, u, v string) (bool, error)
- func (hld *HLDecomposition) LCA(ctx context.Context, u, v string) (lca string, err error)
- func (hld *HLDecomposition) NodeAtPos(i int) int
- func (hld *HLDecomposition) NodeCount() int
- func (hld *HLDecomposition) NodeToIdx(nodeID string) (int, bool)
- func (hld *HLDecomposition) Parent(v int) int
- func (hld *HLDecomposition) PathNodes(ctx context.Context, u, v string) ([]string, error)
- func (hld *HLDecomposition) Pos(v int) int
- func (hld *HLDecomposition) SerializeMetadata() map[string]string
- func (hld *HLDecomposition) Stats() HLDStats
- func (hld *HLDecomposition) SubtreeSize(v int) int
- func (hld *HLDecomposition) Validate() error
- func (hld *HLDecomposition) ValidateGraphHash(g *Graph) error
- func (hld *HLDecomposition) Version() int
- type HierarchicalGraph
- func (hg *HierarchicalGraph) DrillDown(level int, id string) []*Node
- func (hg *HierarchicalGraph) DrillDownWithCRS(ctx context.Context, level int, id string) ([]*Node, crs.TraceStep)
- func (hg *HierarchicalGraph) GetCrossPackageEdges() []*Edge
- func (hg *HierarchicalGraph) GetFilesInPackage(pkg string) []string
- func (hg *HierarchicalGraph) GetInternalEdges() []*Edge
- func (hg *HierarchicalGraph) GetNodesByKind(kind ast.SymbolKind) []*Node
- func (hg *HierarchicalGraph) GetNodesInFile(filePath string) []*Node
- func (hg *HierarchicalGraph) GetNodesInPackage(pkg string) []*Node
- func (hg *HierarchicalGraph) GetNodesInPackageWithCRS(ctx context.Context, pkg string) ([]*Node, crs.TraceStep)
- func (hg *HierarchicalGraph) GetPackageDependencies(pkg string) []string
- func (hg *HierarchicalGraph) GetPackageDependents(pkg string) []string
- func (hg *HierarchicalGraph) GetPackageInfo(pkg string) *PackageInfo
- func (hg *HierarchicalGraph) GetPackageNames() []string
- func (hg *HierarchicalGraph) GetPackageSiblings(nodeID string) []string
- func (hg *HierarchicalGraph) GetPackages() []PackageInfo
- func (hg *HierarchicalGraph) GetPackagesWithCRS(ctx context.Context) ([]PackageInfo, crs.TraceStep)
- func (hg *HierarchicalGraph) GetSiblings(nodeID string) []*Node
- func (hg *HierarchicalGraph) IsProductionFile(filePath string) bool
- func (hg *HierarchicalGraph) RollUp(nodeID string) (string, int)
- func (hg *HierarchicalGraph) SetFileClassification(fc *FileClassification)
- type HotspotNode
- type IncrementalResult
- type InheritanceQueryResult
- type IrreducibleRegion
- type LCAQueryEngine
- type LCAStats
- type LRUCache
- type LSPEnrichmentConfig
- type LSPQuerier
- type LeidenOptions
- type Loop
- type LoopDetectionError
- type LoopNest
- type NoOpCache
- func (NoOpCache) GetDistance(u, v string) (int, bool)
- func (NoOpCache) GetLCA(u, v string) (string, bool)
- func (NoOpCache) GetPath(u, v string) ([]PathSegment, bool)
- func (NoOpCache) Invalidate()
- func (NoOpCache) SetDistance(u, v string, distance int, ttl time.Duration)
- func (NoOpCache) SetLCA(u, v, lca string, ttl time.Duration)
- func (NoOpCache) SetPath(u, v string, segments []PathSegment, ttl time.Duration)
- func (NoOpCache) Stats() CacheStats
- type NoOpCircuitBreaker
- type NoOpRateLimiter
- type Node
- type NodeDiff
- type PackageInfo
- type PageRankNode
- type PageRankOptions
- type PageRankResult
- type PathQueryEngine
- func NewPathQueryEngine(hld *HLDecomposition, segTree *SegmentTree, aggFunc AggregateFunc, ...) (*PathQueryEngine, error)
- func NewPathQueryEngineForForest(forest *HLDForest, segTree *SegmentTree, aggFunc AggregateFunc, ...) (*PathQueryEngine, error)
- func NewPathQueryEngineWithCRS(hld *HLDecomposition, segTree *SegmentTree, aggFunc AggregateFunc, ...) (*PathQueryEngine, error)
- func (pqe *PathQueryEngine) PathGCD(ctx context.Context, u, v string, logger *slog.Logger) (int64, error)
- func (pqe *PathQueryEngine) PathMax(ctx context.Context, u, v string, logger *slog.Logger) (int64, error)
- func (pqe *PathQueryEngine) PathMin(ctx context.Context, u, v string, logger *slog.Logger) (int64, error)
- func (pqe *PathQueryEngine) PathQuery(ctx context.Context, u, v string, logger *slog.Logger) (int64, error)
- func (pqe *PathQueryEngine) PathQueryCacheKey(u, v string) string
- func (pqe *PathQueryEngine) PathSum(ctx context.Context, u, v string, logger *slog.Logger) (int64, error)
- func (pqe *PathQueryEngine) Stats() PathQueryStats
- func (pqe *PathQueryEngine) Validate() error
- type PathQueryEngineOptions
- type PathQueryStats
- type PathResult
- type PathSegment
- type PathUpdateEngine
- func (pue *PathUpdateEngine) PathDecrement(ctx context.Context, u, v string, logger *slog.Logger) error
- func (pue *PathUpdateEngine) PathIncrement(ctx context.Context, u, v string, logger *slog.Logger) error
- func (pue *PathUpdateEngine) PathSet(ctx context.Context, u, v string, value int64, logger *slog.Logger) error
- func (pue *PathUpdateEngine) PathUpdate(ctx context.Context, u, v string, delta int64, logger *slog.Logger) error
- func (pue *PathUpdateEngine) Stats() PathUpdateStats
- type PathUpdateStats
- type ProgressFunc
- type ProgressPhase
- type QueryCacheStats
- type QueryOption
- type QueryOptions
- type QueryResult
- type RateLimiter
- type RateLimiterStats
- type ReducibilityResult
- type ReducibilitySummary
- type RefreshConfig
- type RefreshResult
- type Refresher
- type RefresherOption
- type SESEError
- type SESERegion
- type SESEResult
- type SegmentTree
- func (st *SegmentTree) CacheKey() string
- func (st *SegmentTree) GetValue(ctx context.Context, index int) (int64, error)
- func (st *SegmentTree) MemoryUsage() int
- func (st *SegmentTree) Query(ctx context.Context, left, right int) (int64, error)
- func (st *SegmentTree) RangeUpdate(ctx context.Context, left, right int, delta int64) error
- func (st *SegmentTree) Stats() SegmentTreeStats
- func (st *SegmentTree) Update(ctx context.Context, index int, value int64) error
- func (st *SegmentTree) Validate() error
- type SegmentTreeStats
- type SerializableEdge
- type SerializableGraph
- type SerializableNode
- type SnapshotDiff
- type SnapshotManager
- func (m *SnapshotManager) Delete(ctx context.Context, snapshotID string) error
- func (m *SnapshotManager) List(ctx context.Context, projectHash string, limit int) ([]*SnapshotMetadata, error)
- func (m *SnapshotManager) Load(ctx context.Context, snapshotID string) (*Graph, *SnapshotMetadata, error)
- func (m *SnapshotManager) LoadLatest(ctx context.Context, projectHash string) (*Graph, *SnapshotMetadata, error)
- func (m *SnapshotManager) Save(ctx context.Context, g *Graph, label string) (*SnapshotMetadata, error)
- type SnapshotMetadata
- type StalenessChecker
- type StalenessResult
- type SubtreeQueryEngine
- func (sqe *SubtreeQueryEngine) ClearCache()
- func (sqe *SubtreeQueryEngine) Stats() SubtreeQueryStats
- func (sqe *SubtreeQueryEngine) SubtreeGCD(ctx context.Context, v string) (int64, error)
- func (sqe *SubtreeQueryEngine) SubtreeMax(ctx context.Context, v string) (int64, error)
- func (sqe *SubtreeQueryEngine) SubtreeMin(ctx context.Context, v string) (int64, error)
- func (sqe *SubtreeQueryEngine) SubtreeNodes(ctx context.Context, v string) ([]string, error)
- func (sqe *SubtreeQueryEngine) SubtreeQuery(ctx context.Context, v string) (int64, error)
- func (sqe *SubtreeQueryEngine) SubtreeRange(ctx context.Context, v string) (start, end int, err error)
- func (sqe *SubtreeQueryEngine) SubtreeSum(ctx context.Context, v string) (int64, error)
- func (sqe *SubtreeQueryEngine) Validate() error
- type SubtreeQueryStats
- type SubtreeUpdateEngine
- func (sue *SubtreeUpdateEngine) SubtreeDecrement(ctx context.Context, v string) error
- func (sue *SubtreeUpdateEngine) SubtreeIncrement(ctx context.Context, v string) error
- func (sue *SubtreeUpdateEngine) SubtreeSet(ctx context.Context, v string, value int64) error
- func (sue *SubtreeUpdateEngine) SubtreeUpdate(ctx context.Context, v string, delta int64) error
- func (sue *SubtreeUpdateEngine) UpdateStats() SubtreeUpdateStats
- type SubtreeUpdateStats
- type TraceConfig
- type TraversalResult
Constants ¶
const ( // DefaultMaxMemoryMB is the default memory limit for building (2048MB). // GR-77: Raised from 512 to 2048 so edge extraction completes for // projects up to ~2M LOC. Will be removed when GR-77 (bbolt disk-backed // graph) eliminates the in-memory architecture. DefaultMaxMemoryMB = 2048 // DefaultWorkerCount is the default number of parallel workers. // Set to 0 to use runtime.NumCPU(). DefaultWorkerCount = 0 )
Default builder configuration values.
const ( // DefaultMaxLeidenIterations is the maximum outer loop iterations. DefaultMaxLeidenIterations = 100 // DefaultConvergenceThreshold stops early if modularity gain < this. DefaultConvergenceThreshold = 1e-6 // DefaultMinCommunitySize filters out tiny communities from results. DefaultMinCommunitySize = 1 // DefaultResolution affects community granularity. // Higher values = smaller communities, lower = larger communities. DefaultResolution = 1.0 )
Leiden configuration constants.
const ( // LevelProject represents the project (root) level. LevelProject = 0 // LevelPackage represents the package level. LevelPackage = 1 // LevelFile represents the file level. LevelFile = 2 // LevelSymbol represents the symbol (function/type/etc) level. LevelSymbol = 3 )
HierarchyLevel represents a level in the code hierarchy.
const ( // DefaultDampingFactor is the probability of following a link (vs random jump). // Standard value from the original PageRank paper. DefaultDampingFactor = 0.85 // DefaultMaxIterations is the maximum iterations before stopping. DefaultMaxIterations = 100 // DefaultConvergence is the threshold for convergence detection. // Power iteration stops when max score change < this value. DefaultConvergence = 1e-6 // SmallGraphThreshold is the node count below which we skip convergence checks. SmallGraphThreshold = 10 )
PageRank configuration constants.
const ( // DefaultQueryLimit is the default maximum number of results. DefaultQueryLimit = 1000 // MaxQueryLimit is the maximum allowed limit. MaxQueryLimit = 10000 // DefaultMaxDepth is the default maximum traversal depth. DefaultMaxDepth = 10 // MaxTraversalDepth is the maximum allowed traversal depth. MaxTraversalDepth = 100 )
Query configuration limits.
const ( // DefaultMaxNodes is the default maximum number of nodes a graph can hold. DefaultMaxNodes = 1_000_000 // DefaultMaxEdges is the default maximum number of edges a graph can hold. DefaultMaxEdges = 10_000_000 )
Default configuration values.
const BboltSchemaVersion = "1.0"
BboltSchemaVersion is the version of the bbolt serialization schema. Increment when the bbolt format changes in a breaking way. GR-77a: Decode failure triggers delete + rebuild.
const (
// DefaultMaxDominatorIterations caps convergence iterations.
DefaultMaxDominatorIterations = 100
)
Default configuration for dominator algorithm.
const (
// DefaultQueryCacheSize is the default LRU cache size for query results.
DefaultQueryCacheSize = 1000
)
Query cache configuration.
const GraphSchemaVersion = "1.0"
GraphSchemaVersion is the version of the serialization schema. Increment when the serialization format changes in a breaking way.
const ( // HLDSchemaVersion is the current HLD schema version (A-M1). // Increment when making breaking changes to serialization format. HLDSchemaVersion = 1 )
const IncrementalRefreshThreshold = 0.30
IncrementalRefreshThreshold is the maximum ratio of changed files to total files before falling back to a full rebuild. If changedFiles/totalFiles > 0.30, incremental overhead exceeds full build cost.
const StalenessCheckCacheTTL = 60 * time.Second
StalenessCheckCacheTTL is how long a staleness result is cached before rechecking.
const StalenessCheckSampleSize = 50
StalenessCheckSampleSize is the number of files to spot-check per staleness check. Checking 50 files keeps the check under 50ms on typical systems.
const StalenessErrorThreshold = 0.20
StalenessErrorThreshold is the percentage of changed files that triggers an error log suggesting a graph rebuild. Below this, a warning is logged.
const VirtualExitNodeID = "__virtual_exit__"
VirtualExitNodeID is the sentinel ID for the virtual exit node used when handling graphs with multiple exits. It is filtered from results.
Variables ¶
var ( // ErrGraphFrozen is returned when attempting to modify a frozen graph. // Once Freeze() is called, the graph becomes read-only and no further // nodes or edges can be added. ErrGraphFrozen = errors.New("graph is frozen and cannot be modified") // ErrNodeNotFound is returned when an edge references a non-existent node. // Both source and target nodes must exist before an edge can be created. ErrNodeNotFound = errors.New("node not found") // ErrDuplicateNode is returned when adding a node with an ID that // already exists in the graph. ErrDuplicateNode = errors.New("duplicate node ID") // ErrMaxNodesExceeded is returned when the graph has reached its // configured maximum node capacity. ErrMaxNodesExceeded = errors.New("maximum node count exceeded") // ErrMaxEdgesExceeded is returned when the graph has reached its // configured maximum edge capacity. ErrMaxEdgesExceeded = errors.New("maximum edge count exceeded") // ErrInvalidNode is returned when attempting to add a nil symbol // or a symbol that fails validation. ErrInvalidNode = errors.New("invalid node") // ErrBuildCancelled is returned when a build operation is cancelled via context. ErrBuildCancelled = errors.New("build cancelled") // ErrMemoryLimitExceeded is returned when the builder exceeds its configured // memory limit during graph construction. ErrMemoryLimitExceeded = errors.New("memory limit exceeded") // ErrInvalidEdgeType is returned when an edge type is not valid for the // given source and target node kinds. ErrInvalidEdgeType = errors.New("invalid edge type for node kinds") // ErrNilGraph is returned when attempting to wrap a nil graph. ErrNilGraph = errors.New("graph must not be nil") // ErrGraphNotFrozen is returned when attempting to wrap a graph that // has not been frozen yet. The graph must be frozen (read-only) before // wrapping with HierarchicalGraph. ErrGraphNotFrozen = errors.New("graph must be frozen before wrapping") )
Sentinel errors for graph operations.
var ( ErrInvalidTree = errors.New("graph is not a valid tree") ErrValidationFailed = errors.New("HLD validation failed") )
Sentinel errors for HLD operations.
var ( ErrNodesInDifferentTrees = errors.New("cannot compute LCA: nodes are in different tree components") ErrIterationLimitExceeded = errors.New("LCA iteration limit exceeded") ErrHLDNotInitialized = errors.New("HLD not properly initialized") // I-C1 )
Sentinel errors for LCA operations.
var ( ErrEmptyArray = errors.New("array must not be empty") ErrArrayTooLarge = errors.New("array size exceeds maximum") ErrInvalidAggFunc = errors.New("invalid aggregation function") ErrInvalidRange = errors.New("invalid query range") ErrRangeUpdateNotSUM = errors.New("range update only supported for SUM aggregation") )
Sentinel errors for segment tree operations.
Functions ¶
func GetCorrelationID ¶
GetCorrelationID extracts correlation ID from OpenTelemetry trace context.
Description:
Returns the OTel trace ID as a correlation ID for linking logs and traces. This provides automatic correlation without custom context keys.
Inputs:
- ctx: Context with optional OTel span. Can be nil (returns empty string).
Outputs:
- string: Trace ID in hex format, or empty string if no valid span context.
Thread Safety: Safe for concurrent use.
Example:
correlationID := GetCorrelationID(ctx)
logger.Info("query complete", slog.String("correlation_id", correlationID))
func NullLogger ¶
NullLogger returns a logger that discards all output.
func ProjectHash ¶
ProjectHash returns SHA256(projectRoot)[:16] for use as a key prefix.
Description:
Computes a deterministic, URL-safe hash of the project root path for use as a BadgerDB key prefix. Exported so handlers can convert a project_root query param to the hash used in storage.
Inputs:
projectRoot - The project root path.
Outputs:
string - 16-character hex hash.
func RecordFileMtimes ¶
RecordFileMtimes populates a graph's FileMtimes field by stat'ing all files referenced in the graph's nodes.
Description:
Iterates all nodes in the graph, collects unique file paths, stats each file, and records its modification time. Should be called after Freeze().
Inputs:
- g: The graph to populate. Must not be nil.
- projectRoot: Absolute path to the project root.
Thread Safety: Must be called during build (before concurrent reads).
func ShouldDoIncrementalUpdate ¶
ShouldDoIncrementalUpdate determines whether an incremental update is appropriate.
Description:
Returns true if the ratio of changed files to total files is at or below the IncrementalRefreshThreshold (30%). Returns false if totalFiles is 0 to avoid division by zero.
Inputs:
- changedCount: Number of files that changed since last snapshot.
- totalCount: Total number of files in the project.
Outputs:
- bool: true if incremental update should be used.
Thread Safety: Safe for concurrent use (stateless).
Types ¶
type AggregateFunc ¶
type AggregateFunc int
AggregateFunc defines the type of aggregation operation.
const ( AggregateSUM AggregateFunc = iota // Sum of values AggregateMIN // Minimum value AggregateMAX // Maximum value AggregateGCD // Greatest common divisor )
func (AggregateFunc) Combine ¶
func (f AggregateFunc) Combine(a, b int64) int64
Combine applies the aggregation function to two values.
Description:
Combines two values using the aggregation function. Handles integer overflow for SUM by saturating at MaxInt64/MinInt64.
Thread Safety: Safe for concurrent use (pure function).
func (AggregateFunc) Identity ¶
func (f AggregateFunc) Identity() int64
Identity returns the identity element for the aggregation function.
func (AggregateFunc) String ¶
func (f AggregateFunc) String() string
String returns the string representation of the aggregation function.
type ArticulationResult ¶
type ArticulationResult struct {
// Points contains node IDs that are articulation points.
// An articulation point is a node whose removal increases the number
// of connected components in the graph.
Points []string
// Bridges contains edges whose removal disconnects the graph.
// Each bridge is represented as [fromID, toID].
Bridges [][2]string
// Components is the number of connected components in the graph.
// For a fully connected graph, this equals 1. For disconnected graphs,
// this equals the number of separate subgraphs.
Components int
// NodeCount is the total nodes analyzed.
NodeCount int
// EdgeCount is the total edges analyzed.
EdgeCount int
}
ArticulationResult contains the articulation point analysis.
type BuildProgress ¶
type BuildProgress struct {
// Phase is the current build phase.
Phase ProgressPhase
// FilesTotal is the total number of files to process.
FilesTotal int
// FilesProcessed is the number of files processed so far.
FilesProcessed int
// NodesCreated is the number of nodes created so far.
NodesCreated int
// EdgesCreated is the number of edges created so far.
EdgesCreated int
}
BuildProgress contains progress information during a build.
type BuildResult ¶
type BuildResult struct {
// Graph is the constructed graph. May be partial if errors occurred
// or the build was cancelled.
Graph *Graph
// FileErrors contains errors for files that failed processing.
// Files in this list are not represented in the graph.
FileErrors []FileError
// EdgeErrors contains errors for edges that couldn't be created.
// The graph may still contain valid edges despite these errors.
EdgeErrors []EdgeError
// Stats contains build statistics.
Stats BuildStats
// Incomplete is true if the build was cancelled (via context) or
// stopped due to memory limits. When true, the graph contains
// partial results.
Incomplete bool
}
BuildResult contains the result of a graph build operation.
Build operations are designed to be resilient: individual file failures do not fail the entire build. Instead, partial results are returned along with error information.
func (*BuildResult) HasErrors ¶
func (r *BuildResult) HasErrors() bool
HasErrors returns true if any file or edge errors occurred, including truncated.
func (*BuildResult) Success ¶
func (r *BuildResult) Success() bool
Success returns true if the build completed without errors and is complete.
func (*BuildResult) TotalErrors ¶
func (r *BuildResult) TotalErrors() int
TotalErrors returns the total number of errors (file + edge), including truncated.
type BuildStats ¶
type BuildStats struct {
// FilesProcessed is the number of files successfully processed.
FilesProcessed int
// FilesFailed is the number of files that failed processing.
FilesFailed int
// NodesCreated is the number of nodes added to the graph.
NodesCreated int
// EdgesCreated is the number of edges added to the graph.
EdgesCreated int
// PlaceholderNodes is the number of placeholder nodes created for
// external/unresolved symbols.
PlaceholderNodes int
// AmbiguousResolves is the number of call resolutions that matched
// multiple symbols (over-approximated by creating edges to all).
AmbiguousResolves int
// GoInterfaceEdges is the number of EdgeTypeImplements edges created
// via Go interface implementation detection (method-set matching).
// See GR-40 for details.
GoInterfaceEdges int
// CallEdgesResolved is the number of EdgeTypeCalls edges where the
// target was successfully resolved to an existing symbol.
// See GR-41 for details.
CallEdgesResolved int
// CallEdgesUnresolved is the number of EdgeTypeCalls edges where the
// target could not be resolved and a placeholder was created.
// See GR-41 for details.
CallEdgesUnresolved int
// NamedImportEdgesResolved is the number of EdgeTypeReferences edges
// created by the named import resolution pass (GR-62). Each represents
// a "from X import Y" statement where Y was resolved to an in-project
// symbol node.
NamedImportEdgesResolved int
// CommonJSImportEdgesResolved is the number of EdgeTypeReferences edges
// created by the CommonJS import alias resolution pass (IT-06d Bug D).
// Each represents a "var X = require('./module')" statement where the
// imported module's exported class was found and linked.
CommonJSImportEdgesResolved int
// DynamicImportEdgesResolved is the number of EdgeTypeReferences edges
// created by the dynamic import() resolution pass (IT-06e Bug 4).
// Each represents an import('./module') call where the dynamically loaded
// module's exported class was found and linked to the importing file.
DynamicImportEdgesResolved int
// DecoratorArgEdgesResolved is the number of EdgeTypeReferences edges
// created by the decorator argument resolution pass (IT-06e Bug 5).
// Each represents a class name in a @Module({providers: [X]}) or
// @NgModule({imports: [X]}) decorator array that resolved to an in-project symbol.
DecoratorArgEdgesResolved int
// DurationMilli is the total build time in milliseconds.
// NOTE: For fast builds (< 1ms), this rounds to 0. Use DurationMicro for precision.
DurationMilli int64
// DurationMicro is the total build time in microseconds (for sub-millisecond precision).
DurationMicro int64
// FileErrorsTruncated is the number of file errors dropped after the cap was reached.
// See maxErrorSliceLen.
FileErrorsTruncated int
// EdgeErrorsTruncated is the number of edge errors dropped after the cap was reached.
// See maxErrorSliceLen.
EdgeErrorsTruncated int
// ValidationBypassed is the number of edge validation checks that were bypassed
// because one or both symbols were not in the symbolsByID map (e.g., placeholder nodes).
// GR-71: Tracked for observability — a high count may indicate placeholder ID issues.
ValidationBypassed int
// ValidationRejected is the number of edges rejected by validateEdgeType because
// the edge type was invalid for the source/target node kinds.
// GR-71: Tracked for observability — helps diagnose false-positive edge creation.
ValidationRejected int
// LSPEnrichment contains statistics from the LSP enrichment phase (GR-74).
// Zero-valued if LSP enrichment was not configured or not run.
LSPEnrichment EnrichmentStats
}
BuildStats contains statistics about a build operation.
type Builder ¶
type Builder struct {
// contains filtered or unexported fields
}
Builder constructs code graphs from parsed AST results.
The builder is stateless and can be reused across multiple builds. Each Build() call creates a new graph.
Thread Safety:
Builder is safe for concurrent use. Each Build() call operates independently with its own internal state.
func NewBuilder ¶
func NewBuilder(opts ...BuilderOption) *Builder
NewBuilder creates a new Builder with the given options.
Example:
builder := NewBuilder(
WithProjectRoot("/path/to/project"),
WithMaxMemoryMB(1024),
)
func (*Builder) Build ¶
func (b *Builder) Build(ctx context.Context, results []*ast.ParseResult) (*BuildResult, error)
Build constructs a graph from the given parse results.
Description:
Processes all parse results, creating nodes for symbols and edges for their relationships. The build is resilient to individual file failures - partial results are returned even on errors.
Inputs:
ctx - Context for cancellation. Build checks context periodically. results - Parse results from AST parsing. Nil entries are skipped with error.
Outputs:
*BuildResult - Contains the graph, any errors, and build statistics. Always non-nil, even when error is returned (partial result for debugging). error - Non-nil when context is cancelled/expired or memory limit exceeded. Wraps the underlying error (ctx.Err() or ErrMemoryLimitExceeded).
Build Phases:
- COLLECT: Validate and add all symbols as nodes
- EXTRACT EDGES: Create edges for imports, calls, implements, etc.
- LSP ENRICHMENT (optional): Resolve placeholder targets via LSP definition lookup (GR-74)
- FINALIZE: Freeze graph and compute statistics
type BuilderOption ¶
type BuilderOption func(*BuilderOptions)
BuilderOption is a functional option for configuring Builder.
func WithBuilderMaxEdges ¶
func WithBuilderMaxEdges(n int) BuilderOption
WithBuilderMaxEdges sets the maximum number of edges.
func WithBuilderMaxNodes ¶
func WithBuilderMaxNodes(n int) BuilderOption
WithBuilderMaxNodes sets the maximum number of nodes.
func WithLSPEnrichment ¶
func WithLSPEnrichment(config *LSPEnrichmentConfig) BuilderOption
WithLSPEnrichment configures LSP-based placeholder resolution.
Description:
GR-74: When set, the builder runs an LSP enrichment phase between edge extraction and finalization. This phase queries language servers for unresolved placeholder targets, converting them into real edges.
Inputs:
config - LSP enrichment configuration. If nil or Querier is nil, enrichment is skipped.
func WithMaxMemoryMB ¶
func WithMaxMemoryMB(mb int) BuilderOption
WithMaxMemoryMB sets the maximum memory usage in megabytes.
func WithProgressCallback ¶
func WithProgressCallback(fn ProgressFunc) BuilderOption
WithProgressCallback sets the progress callback function.
func WithProjectRoot ¶
func WithProjectRoot(root string) BuilderOption
WithProjectRoot sets the project root path.
func WithWorkerCount ¶
func WithWorkerCount(n int) BuilderOption
WithWorkerCount sets the number of parallel workers.
type BuilderOptions ¶
type BuilderOptions struct {
// ProjectRoot is the absolute path to the project root directory.
ProjectRoot string
// MaxMemoryMB is the maximum memory usage in megabytes.
// Build will stop with partial results if exceeded.
// Default: 512
MaxMemoryMB int
// WorkerCount is the number of parallel workers for edge extraction.
// Default: runtime.NumCPU()
WorkerCount int
// ProgressCallback is called periodically with build progress.
// May be nil. GR-73: Must be goroutine-safe — with WorkerCount > 1,
// this callback may be invoked from multiple goroutines concurrently.
// Progress reports may arrive out-of-order.
ProgressCallback ProgressFunc
// MaxNodes is the maximum number of nodes (passed to Graph).
MaxNodes int
// MaxEdges is the maximum number of edges (passed to Graph).
MaxEdges int
// LSPEnrichment configures optional LSP-based placeholder resolution.
// GR-74: When non-nil with a valid Querier, an enrichment phase runs
// between edge extraction and finalization.
LSPEnrichment *LSPEnrichmentConfig
}
BuilderOptions configures Builder behavior.
func DefaultBuilderOptions ¶
func DefaultBuilderOptions() BuilderOptions
DefaultBuilderOptions returns sensible defaults.
type CRSGraphAdapter ¶
type CRSGraphAdapter struct {
// contains filtered or unexported fields
}
CRSGraphAdapter provides read-only access to the code graph from CRS activities.
Description:
CRSGraphAdapter wraps the HierarchicalGraph, GraphAnalytics, and SymbolIndex to provide a clean interface for CRS activities to query the code graph. It handles: - Lifecycle management (closed state detection) - Generation tracking for staleness detection - Analytics caching with TTL and singleflight deduplication - O(1) symbol lookups via SymbolIndex - OTel tracing for all queries
Thread Safety: All methods are safe for concurrent use.
func NewCRSGraphAdapter ¶
func NewCRSGraphAdapter(g *HierarchicalGraph, idx *index.SymbolIndex, generation int64, refreshTime int64, config *crs.GraphQueryConfig) (*CRSGraphAdapter, error)
NewCRSGraphAdapter creates a new adapter for querying the graph from CRS.
Description:
Creates an adapter that wraps the graph, analytics, and optional symbol index for CRS use. The adapter tracks the graph generation for staleness detection.
Inputs:
- g: The hierarchical graph to wrap. Must not be nil.
- idx: Optional symbol index for O(1) lookups. May be nil.
- generation: The graph generation number.
- refreshTime: When the graph was last refreshed (Unix milliseconds UTC).
- config: Optional configuration. Uses defaults if nil.
Outputs:
- *CRSGraphAdapter: The adapter. Never nil if g is not nil.
- error: Non-nil if g is nil.
Example:
adapter, err := NewCRSGraphAdapter(graph, symbolIndex, gen, refreshTime, nil)
if err != nil {
return fmt.Errorf("creating graph adapter: %w", err)
}
defer adapter.Close()
Limitations:
- Adapter does not own the graph or index lifecycle
- Graph mutations after adapter creation may cause stale results
Thread Safety: The returned adapter is safe for concurrent use.
func (*CRSGraphAdapter) Analytics ¶
func (a *CRSGraphAdapter) Analytics() crs.GraphAnalyticsQuery
Analytics returns the analytics query interface.
Outputs:
- crs.GraphAnalyticsQuery: The analytics interface. Never nil.
Thread Safety: Safe for concurrent use.
func (*CRSGraphAdapter) CallEdgeCount ¶
func (a *CRSGraphAdapter) CallEdgeCount(ctx context.Context) (int, error)
CallEdgeCount returns the number of CALLS edges in the graph.
Description:
Counts edges of type EdgeTypeCalls. This is used by the dependency index Size() method. Results are cached and invalidated when InvalidateCache() is called.
Inputs:
- ctx: Context for cancellation.
Outputs:
- int: Number of call edges.
- error: Non-nil on context cancellation or if adapter is closed.
Thread Safety: Safe for concurrent use.
func (*CRSGraphAdapter) Close ¶
func (a *CRSGraphAdapter) Close() error
Close releases resources held by the adapter.
Description:
Must be called when the adapter is no longer needed to prevent resource leaks. After Close, all methods return ErrGraphQueryClosed.
Thread Safety: Safe for concurrent use. Idempotent.
func (*CRSGraphAdapter) EdgeCount ¶
func (a *CRSGraphAdapter) EdgeCount() int
EdgeCount returns the number of edges in the graph.
Thread Safety: Safe for concurrent use.
func (*CRSGraphAdapter) FindCallees ¶
FindCallees returns symbols that the given symbol calls.
Description:
Finds all symbols that are called by the source symbol via outgoing call edges. GR-10: Uses LRU cache to avoid recomputation for repeated queries.
Inputs:
- ctx: Context for cancellation. Must not be nil.
- symbolID: The symbol to find callees for.
Outputs:
- []*ast.Symbol: Callee symbols. Empty slice if none found.
- error: Non-nil on graph query failure or adapter closed.
Limitations:
- GR-10 Review (I-1): Results may be cached. Callers MUST NOT mutate the returned slice or its elements, as this would corrupt the cache.
- Truncated results (when limit is reached) are not cached.
Thread Safety: Safe for concurrent use.
func (*CRSGraphAdapter) FindCallers ¶
FindCallers returns symbols that call the given symbol.
Description:
Finds all symbols that have outgoing call edges to the target symbol. GR-10: Uses LRU cache to avoid recomputation for repeated queries. If SymbolIndex is available, uses O(1) lookup; otherwise falls back to graph traversal.
Inputs:
- ctx: Context for cancellation. Must not be nil.
- symbolID: The symbol to find callers for.
Outputs:
- []*ast.Symbol: Caller symbols. Empty slice if none found.
- error: Non-nil on graph query failure or adapter closed.
Limitations:
- GR-10 Review (I-1): Results may be cached. Callers MUST NOT mutate the returned slice or its elements, as this would corrupt the cache.
- Truncated results (when limit is reached) are not cached.
Thread Safety: Safe for concurrent use.
func (*CRSGraphAdapter) FindImplementations ¶
func (a *CRSGraphAdapter) FindImplementations(ctx context.Context, interfaceName string) ([]*ast.Symbol, error)
FindImplementations returns types that implement the given interface.
Description:
Finds all types that implement the specified interface by name. Searches across all matching interface definitions.
Inputs:
- ctx: Context for cancellation. Must not be nil.
- interfaceName: The interface name to find implementations for.
Outputs:
- []*ast.Symbol: Implementing types. Empty slice if none found.
- error: Non-nil on graph query failure or adapter closed.
Thread Safety: Safe for concurrent use.
func (*CRSGraphAdapter) FindReferences ¶
func (a *CRSGraphAdapter) FindReferences(ctx context.Context, symbolID string) ([]*ast.Symbol, error)
FindReferences returns symbols that reference the given symbol.
Inputs:
- ctx: Context for cancellation. Must not be nil.
- symbolID: The symbol to find references for.
Outputs:
- []*ast.Symbol: Referencing symbols. Empty slice if none found.
- error: Non-nil on graph query failure or adapter closed.
Thread Safety: Safe for concurrent use.
func (*CRSGraphAdapter) FindSymbolByID ¶
FindSymbolByID returns a symbol by its unique ID.
Inputs:
- ctx: Context for cancellation. Must not be nil.
- id: The unique symbol ID.
Outputs:
- *ast.Symbol: The symbol, or nil if not found.
- bool: True if symbol was found.
- error: Non-nil on context cancellation or adapter closed.
Thread Safety: Safe for concurrent use.
func (*CRSGraphAdapter) FindSymbolsByKind ¶
func (a *CRSGraphAdapter) FindSymbolsByKind(ctx context.Context, kind ast.SymbolKind) ([]*ast.Symbol, error)
FindSymbolsByKind returns all symbols of the given kind.
Inputs:
- ctx: Context for cancellation. Must not be nil.
- kind: The symbol kind to filter by.
Outputs:
- []*ast.Symbol: Matching symbols. Empty slice if none found.
- error: Non-nil on context cancellation or adapter closed.
Thread Safety: Safe for concurrent use.
func (*CRSGraphAdapter) FindSymbolsByName ¶
func (a *CRSGraphAdapter) FindSymbolsByName(ctx context.Context, name string) ([]*ast.Symbol, error)
FindSymbolsByName returns all symbols with the given name.
Description:
GR-06: Uses nodesByName secondary index for O(1) lookup instead of O(V) scan. Multiple symbols can share a name (e.g., "Setup" in different packages).
Inputs:
- ctx: Context for cancellation. Must not be nil.
- name: The symbol name to search for.
Outputs:
- []*ast.Symbol: Matching symbols. Empty slice if none found.
- error: Non-nil on context cancellation or adapter closed.
Thread Safety: Safe for concurrent use.
func (*CRSGraphAdapter) FindSymbolsInFile ¶
func (a *CRSGraphAdapter) FindSymbolsInFile(ctx context.Context, filePath string) ([]*ast.Symbol, error)
FindSymbolsInFile returns all symbols in the given file.
Inputs:
- ctx: Context for cancellation. Must not be nil.
- filePath: The file path to search in.
Outputs:
- []*ast.Symbol: Symbols in the file. Empty slice if none found.
- error: Non-nil on context cancellation or adapter closed.
Thread Safety: Safe for concurrent use.
func (*CRSGraphAdapter) Generation ¶
func (a *CRSGraphAdapter) Generation() int64
Generation returns the graph generation this adapter was created with.
Description:
Use for staleness detection. If the current graph generation is higher than this value, the adapter may return stale data.
Thread Safety: Safe for concurrent use.
func (*CRSGraphAdapter) GetCallChain ¶
func (a *CRSGraphAdapter) GetCallChain(ctx context.Context, fromID, toID string, maxDepth int) ([]string, error)
GetCallChain returns the call chain from source to target.
Description:
Finds the path from source to target following call edges. Uses BFS for path finding to ensure shortest path.
Inputs:
- ctx: Context for cancellation. Must not be nil.
- fromID: The source symbol ID.
- toID: The target symbol ID.
- maxDepth: Maximum traversal depth. Clamped to [1, 100].
Outputs:
- []string: Symbol IDs in the call chain. Empty if no path found.
- error: Non-nil on graph query failure or adapter closed.
Example:
path, err := adapter.GetCallChain(ctx, "main.go:10:main", "util.go:20:helper", 10)
if err != nil {
return fmt.Errorf("getting call chain: %w", err)
}
Limitations:
- Only follows call edges, not other edge types
- maxDepth is clamped to prevent runaway traversal
Thread Safety: Safe for concurrent use.
func (*CRSGraphAdapter) GetEdgeCountByFile ¶
GetEdgeCountByFile returns the count of edges with Location in the given file.
Description:
GR-09: Uses secondary index for O(1) count without copying edges. More efficient than len(GetEdgesByFile()) for just getting counts.
Inputs:
- ctx: Context for cancellation. Must not be nil.
- filePath: The file path to count edges for.
Outputs:
- int: Number of edges with Location in the file.
- error: Non-nil on context cancellation or adapter closed.
Thread Safety: Safe for concurrent use.
func (*CRSGraphAdapter) GetEdgesByFile ¶
GetEdgesByFile returns all edges with Location in the given file.
Description:
GR-09: Uses edgesByFile secondary index for O(1) lookup instead of O(E) scan. Useful for file-scoped dependency analysis. Note: Returns edges by their Location.FilePath (where the edge is expressed in code), not by source or target node file.
Inputs:
- ctx: Context for cancellation. Must not be nil.
- filePath: The file path to find edges for.
Outputs:
- []*Edge: Edges with Location in the file. Empty slice if none found.
- error: Non-nil on context cancellation or adapter closed.
Thread Safety: Safe for concurrent use.
func (*CRSGraphAdapter) HasCycleFrom ¶
HasCycleFrom checks if there's a cycle in the call graph starting from the given symbol.
Description:
Uses depth-first search with a recursion stack to detect cycles in the call graph. Only follows EdgeTypeCalls edges.
Inputs:
- ctx: Context for cancellation. Must not be nil.
- symbolID: The starting symbol ID to check for cycles from.
Outputs:
- bool: True if a cycle is detected, false otherwise.
- error: Non-nil on context cancellation or if adapter is closed.
Example:
hasCycle, err := adapter.HasCycleFrom(ctx, "pkg.Function")
if err != nil {
return fmt.Errorf("cycle check: %w", err)
}
if hasCycle {
log.Warn("recursive call detected")
}
Limitations:
- Only detects cycles through direct CALLS edges
- May miss cycles through function pointers or interfaces
- Performance: O(V + E) where V=nodes reachable, E=edges traversed
Thread Safety: Safe for concurrent use.
func (*CRSGraphAdapter) InvalidateCache ¶
func (a *CRSGraphAdapter) InvalidateCache()
InvalidateCache clears all cached analytics and query results.
Description:
Call this when the underlying graph has been refreshed to ensure subsequent queries return fresh data. This is typically called by the graph refresh event handler.
Thread Safety: Safe for concurrent use.
func (*CRSGraphAdapter) LastRefreshTime ¶
func (a *CRSGraphAdapter) LastRefreshTime() int64
LastRefreshTime returns when the graph was last refreshed (Unix milliseconds UTC).
Thread Safety: Safe for concurrent use.
func (*CRSGraphAdapter) NodeCount ¶
func (a *CRSGraphAdapter) NodeCount() int
NodeCount returns the number of nodes in the graph.
Thread Safety: Safe for concurrent use.
func (*CRSGraphAdapter) QueryCacheStats ¶
func (a *CRSGraphAdapter) QueryCacheStats() QueryCacheStats
QueryCacheStats returns statistics for the query caches.
Description:
Returns hit/miss counts and sizes for all query caches (callers, callees, paths). Useful for monitoring cache effectiveness and sizing decisions.
Outputs:
- QueryCacheStats: Cache statistics snapshot.
Example:
stats := adapter.QueryCacheStats()
if stats.HitRate < 0.5 {
log.Warn("low cache hit rate", "rate", stats.HitRate)
}
Limitations:
- Stats are a point-in-time snapshot; may be slightly stale
- Hit rate calculation uses integer counts, may lose precision
Thread Safety: Safe for concurrent use. Returns atomic snapshot.
func (*CRSGraphAdapter) ShortestPath ¶
ShortestPath returns the shortest path between two symbols.
Description:
Finds the shortest path between two symbols considering all edge types. Uses BFS for unweighted shortest path. GR-10: Uses LRU cache to avoid recomputation for repeated queries.
Inputs:
- ctx: Context for cancellation. Must not be nil.
- fromID: The source symbol ID.
- toID: The target symbol ID.
Outputs:
- []string: Symbol IDs in the path. Empty if no path found.
- error: Non-nil on graph query failure or adapter closed.
Limitations:
- GR-10 Review (I-1): Results may be cached. Callers MUST NOT mutate the returned slice, as this would corrupt the cache.
- Returns error if source or target node does not exist.
Thread Safety: Safe for concurrent use.
type CacheStats ¶
type CacheStats struct {
Hits int64 // Total cache hits
Misses int64 // Total cache misses
Evictions int64 // Total evictions
Size int // Current number of cached entries
MemoryKB int // Approximate memory usage in KB
}
CacheStats contains cache performance metrics.
type CircuitBreaker ¶
type CircuitBreaker interface {
// Execute runs the function if circuit is closed, returns error if open.
Execute(fn func() error) error
// State returns current circuit state.
State() CircuitState
// Stats returns circuit breaker statistics.
Stats() CircuitBreakerStats
}
CircuitBreaker prevents cascading failures.
Description:
Implements circuit breaker pattern with three states: - Closed: Normal operation, all requests pass through - Open: Failing fast, no requests allowed - Half-Open: Testing if service recovered
Thread Safety: All methods must be safe for concurrent use.
type CircuitBreakerStats ¶
type CircuitBreakerStats struct {
State CircuitState
TotalRequests int64
SuccessfulRequests int64
FailedRequests int64
RejectedRequests int64 // Rejected due to open circuit
ConsecutiveFailures int64
LastStateChange int64 // Unix milliseconds UTC
}
CircuitBreakerStats contains circuit breaker metrics.
type CircuitState ¶
type CircuitState int
CircuitState represents circuit breaker state.
const ( CircuitClosed CircuitState = 0 // Normal operation CircuitOpen CircuitState = 1 // Failing fast CircuitHalfOpen CircuitState = 2 // Testing recovery )
func (CircuitState) String ¶
func (s CircuitState) String() string
type Community ¶
type Community struct {
// ID is the unique identifier for this community.
ID int `json:"id"`
// Nodes contains the node IDs in this community.
Nodes []string `json:"nodes"`
// DominantPackage is the most common package in this community.
DominantPackage string `json:"dominant_package"`
// InternalEdges is the count of edges within this community.
InternalEdges int `json:"internal_edges"`
// ExternalEdges is the count of edges to other communities.
ExternalEdges int `json:"external_edges"`
// Connectivity is the internal density measure (internal / (internal + external)).
Connectivity float64 `json:"connectivity"`
}
Community represents a detected code module.
type CommunityResult ¶
type CommunityResult struct {
// Communities contains all detected communities.
Communities []Community `json:"communities"`
// Modularity is the final modularity score Q.
// Range [0, 1] where higher is better.
Modularity float64 `json:"modularity"`
// Iterations is the number of outer loop passes completed.
Iterations int `json:"iterations"`
// Converged indicates whether the algorithm converged before MaxIterations.
Converged bool `json:"converged"`
// NodeCount is the total nodes analyzed.
NodeCount int `json:"node_count"`
// EdgeCount is the total edges analyzed.
EdgeCount int `json:"edge_count"`
}
CommunityResult contains the full Leiden output.
func (*CommunityResult) GetCommunityForNode ¶
func (r *CommunityResult) GetCommunityForNode(nodeID string) (int, bool)
GetCommunityForNode returns the community ID for a node.
Inputs:
nodeID - The node ID to look up.
Outputs:
int - The community ID. bool - True if the node was found in a community.
func (*CommunityResult) GetCommunityMembers ¶
func (r *CommunityResult) GetCommunityMembers(communityID int) []string
GetCommunityMembers returns all nodes in a community.
Inputs:
communityID - The community ID to look up.
Outputs:
[]string - Node IDs in the community. Empty if not found.
type ControlDependence ¶
type ControlDependence struct {
// Dependencies maps node → nodes it is control-dependent on.
// "What controls whether this node executes?"
Dependencies map[string][]string
// Dependents maps node → nodes that are control-dependent on it.
// "What does this node control the execution of?"
Dependents map[string][]string
// EdgeCount is the total control dependence edges.
EdgeCount int
// NodeCount is the total nodes analyzed.
NodeCount int
// NodesWithDependencies is the count of nodes with non-empty Dependencies.
NodesWithDependencies int
// NodesWithDependents is the count of nodes with non-empty Dependents.
NodesWithDependents int
// MaxDependencies is the most dependencies any single node has.
MaxDependencies int
// MaxDependents is the most dependents any single controller has.
MaxDependents int
// PostDomTree is the underlying post-dominator tree used.
PostDomTree *DominatorTree
}
ControlDependence represents the control dependence graph.
A node B is control-dependent on A if:
- There exists a path from A to B where A is not post-dominated by B
- B post-dominates all nodes on the path after A
This tells us "which conditionals control whether B executes."
Thread Safety: Safe for concurrent use after construction.
func (*ControlDependence) ControlDependencyChain ¶
func (cd *ControlDependence) ControlDependencyChain(nodeID string, maxDepth int) []string
ControlDependencyChain returns the transitive chain of control dependencies.
Description:
Starting from nodeID, walks up the control dependency chain collecting all controllers. Uses BFS to find all controllers at each level. Stops at maxDepth or when no more dependencies exist.
Inputs:
- nodeID: The starting node to find dependencies for.
- maxDepth: Maximum depth to traverse. Must be > 0.
Outputs:
- []string: Chain of controllers in BFS order (closest first). Returns nil if receiver is nil, maxDepth <= 0, or no dependencies exist.
Limitations:
- Does not include nodeID itself in the output.
- Cycles are handled by tracking visited nodes.
- BFS order means all depth-1 controllers appear before depth-2, etc.
Thread Safety: Safe for concurrent use.
Complexity: O(edges traversed) bounded by maxDepth.
func (*ControlDependence) GetDependencies ¶
func (cd *ControlDependence) GetDependencies(nodeID string) []string
GetDependencies returns what controls this node.
Description:
Returns the list of nodes that control whether this node executes. A control dependency from A to B means "A's branch decision determines whether B runs".
Inputs:
- nodeID: The node to query dependencies for. Must be a valid node ID.
Outputs:
- []string: List of controlling nodes, or nil if the node has no dependencies or the receiver is nil.
Limitations:
- Returns nil for nodes not in the post-dominator tree.
- The returned slice should not be modified by the caller.
Thread Safety: Safe for concurrent use.
Complexity: O(1) map lookup.
func (*ControlDependence) GetDependents ¶
func (cd *ControlDependence) GetDependents(nodeID string) []string
GetDependents returns what this node controls.
Description:
Returns the list of nodes whose execution is controlled by this node. If this node is a branch point, its dependents are the nodes that only execute on certain branches.
Inputs:
- nodeID: The node to query dependents for. Must be a valid node ID.
Outputs:
- []string: List of controlled nodes, or nil if the node has no dependents or the receiver is nil.
Limitations:
- Returns nil for nodes that are not controllers.
- The returned slice should not be modified by the caller.
Thread Safety: Safe for concurrent use.
Complexity: O(1) map lookup.
func (*ControlDependence) IsController ¶
func (cd *ControlDependence) IsController(nodeID string) bool
IsController returns true if this node controls other nodes.
Description:
Checks whether this node is a control point (branch/decision point) that determines whether other nodes execute.
Inputs:
- nodeID: The node to check. Must be a valid node ID.
Outputs:
- bool: True if this node controls at least one other node, false otherwise.
Limitations:
- Returns false if receiver is nil.
Thread Safety: Safe for concurrent use.
Complexity: O(1) map lookup and length check.
type ControlDependenceError ¶
type ControlDependenceError struct {
Message string
}
ControlDependenceError represents an error in control dependence computation.
func (*ControlDependenceError) Error ¶
func (e *ControlDependenceError) Error() string
type CouplingMetrics ¶
type CouplingMetrics struct {
// Package is the package being measured.
Package string
// Afferent is the number of packages that depend on this package (Ca).
// Higher = more packages would be affected by changes here.
Afferent int
// Efferent is the number of packages this package depends on (Ce).
// Higher = more dependencies to track.
Efferent int
// Instability is Ce / (Ca + Ce), range [0, 1].
// 0 = maximally stable (many dependents, few dependencies)
// 1 = maximally unstable (few dependents, many dependencies)
Instability float64
// AbstractTypes is the count of interfaces and abstract types.
AbstractTypes int
// ConcreteTypes is the count of concrete types.
ConcreteTypes int
// Abstractness is AbstractTypes / (AbstractTypes + ConcreteTypes), range [0, 1].
Abstractness float64
}
CouplingMetrics contains package coupling analysis results.
type CyclicDependency ¶
type CyclicDependency struct {
// Nodes contains the node IDs in the cycle (in cycle order).
Nodes []string
// Packages contains the unique packages involved in the cycle.
Packages []string
// Length is the number of nodes in the cycle.
Length int
}
CyclicDependency represents a cycle in the dependency graph.
type DeadCodeNode ¶
type DeadCodeNode struct {
// Node is the graph node.
Node *Node
// Reason explains why this was flagged as dead code.
Reason string
}
DeadCodeNode represents a potentially unused symbol.
type DiffSummary ¶
type DiffSummary struct {
// TotalChanges is the total number of changes (added + removed + modified nodes + edge changes).
TotalChanges int `json:"total_changes"`
// FilesAffected is the number of distinct files with changed symbols.
FilesAffected int `json:"files_affected"`
// ChangeRatio is the fraction of nodes that changed (0.0 to 1.0).
ChangeRatio float64 `json:"change_ratio"`
}
DiffSummary contains aggregate statistics about a diff.
type DirtyEntry ¶
type DirtyEntry struct {
// Path is the file path (absolute or project-relative).
Path string
// MarkedAt is when the file was marked dirty.
MarkedAt time.Time
// Source indicates how the file became dirty ("agent", "watcher", "manual").
Source string
}
DirtyEntry contains metadata about a dirty file.
type DirtyTracker ¶
type DirtyTracker struct {
// contains filtered or unexported fields
}
DirtyTracker tracks files modified during agent execution.
Description:
Tracks which files have been modified and need graph refresh. Used by the execute phase to trigger incremental graph updates before tool queries return stale data.
Thread Safety:
All methods are safe for concurrent use.
func NewDirtyTracker ¶
func NewDirtyTracker() *DirtyTracker
NewDirtyTracker creates a new tracker.
Description:
Creates a tracker for monitoring file modifications during agent execution. Tracks which files need re-parsing.
Inputs:
None.
Outputs:
*DirtyTracker - Ready to track file changes.
Thread Safety:
The returned tracker is safe for concurrent use.
func (*DirtyTracker) Clear ¶
func (d *DirtyTracker) Clear(paths []string) int
Clear removes specified paths from the dirty set.
Description:
Called after successful refresh to clear the dirty files that were refreshed. Only clears the specified paths.
Inputs:
paths - Paths to clear from dirty set.
Outputs:
int - Number of paths actually cleared.
Thread Safety:
Safe for concurrent use.
func (*DirtyTracker) ClearAll ¶
func (d *DirtyTracker) ClearAll() int
ClearAll removes all paths from the dirty set.
Description:
Clears the entire dirty set. Use after a full refresh or when resetting the tracker.
Outputs:
int - Number of paths cleared.
Thread Safety:
Safe for concurrent use.
func (*DirtyTracker) Count ¶
func (d *DirtyTracker) Count() int
Count returns the number of dirty files.
Outputs:
int - Number of dirty files.
Thread Safety:
Safe for concurrent use.
func (*DirtyTracker) Disable ¶
func (d *DirtyTracker) Disable()
Disable turns off dirty tracking.
Description:
When disabled, MarkDirty calls are no-ops. Useful for read-only sessions where no file modifications are expected.
Thread Safety:
Safe for concurrent use.
func (*DirtyTracker) Enable ¶
func (d *DirtyTracker) Enable()
Enable enables dirty tracking.
Thread Safety:
Safe for concurrent use.
func (*DirtyTracker) GetDirtyEntries ¶
func (d *DirtyTracker) GetDirtyEntries() []DirtyEntry
GetDirtyEntries returns all dirty entries with metadata.
Description:
Returns full metadata about dirty files including timestamps and sources. Does NOT clear the dirty set.
Inputs:
None.
Outputs:
[]DirtyEntry - Dirty file entries. Empty slice if none.
Thread Safety:
Safe for concurrent use. Returns copies.
func (*DirtyTracker) GetDirtyFiles ¶
func (d *DirtyTracker) GetDirtyFiles() []string
GetDirtyFiles returns all dirty file paths without clearing.
Description:
Returns a copy of the dirty file paths. Does NOT clear the dirty set. Use Clear() after successful refresh.
Inputs:
None.
Outputs:
[]string - Paths of dirty files. Empty slice if none.
Thread Safety:
Safe for concurrent use. Returns a copy.
func (*DirtyTracker) HasDirty ¶
func (d *DirtyTracker) HasDirty() bool
HasDirty returns true if any files are marked dirty.
Description:
Quick check before tool execution to determine if incremental refresh is needed.
Inputs:
None.
Outputs:
bool - True if dirty files exist.
Thread Safety:
Safe for concurrent use.
func (*DirtyTracker) IsEnabled ¶
func (d *DirtyTracker) IsEnabled() bool
IsEnabled returns true if tracking is enabled.
Thread Safety:
Safe for concurrent use.
func (*DirtyTracker) MarkDirty ¶
func (d *DirtyTracker) MarkDirty(path string)
MarkDirty marks a file as modified and needing refresh.
Description:
Called by tools after writing to a file. Records the file path and timestamp for later incremental refresh.
Inputs:
path - Absolute or project-relative file path.
Outputs:
None.
Thread Safety:
Safe for concurrent use.
func (*DirtyTracker) MarkDirtyFromWatcher ¶
func (d *DirtyTracker) MarkDirtyFromWatcher(change FileChange)
MarkDirtyFromWatcher handles a file change from the FileWatcher.
Description:
Integrates with the existing FileWatcher to mark files dirty when external changes are detected. Skips removed files.
Inputs:
change - The file change event from FileWatcher.
Outputs:
None.
Thread Safety:
Safe for concurrent use.
func (*DirtyTracker) MarkDirtyWithSource ¶
func (d *DirtyTracker) MarkDirtyWithSource(path, source string)
MarkDirtyWithSource marks a file dirty with a specific source.
Description:
Same as MarkDirty but allows specifying the source of the change. Useful for distinguishing between agent modifications and external file watcher events.
Inputs:
path - Absolute or project-relative file path.
source - The source of the modification ("agent", "watcher", "manual").
Outputs:
None.
Thread Safety:
Safe for concurrent use.
type DiskGraph ¶
type DiskGraph struct {
// contains filtered or unexported fields
}
DiskGraph provides read access to a graph persisted in a bbolt file.
Description:
DiskGraph opens a bbolt file created by MaterializeToDisk and provides metadata accessors and individual node lookups. For full query support, use LoadAsGraph to reconstruct an in-memory Graph.
Thread Safety:
Safe for concurrent reads. bbolt supports concurrent read transactions. Close must not be called concurrently with reads.
func OpenDiskGraph ¶
OpenDiskGraph opens a bbolt graph file and validates its schema version.
Description:
Opens the bbolt file in read-only mode, reads metadata from the meta bucket, and validates the schema version matches BboltSchemaVersion. The caller must call Close when done.
Inputs:
path - Absolute path to the bbolt file. Must exist.
Outputs:
*DiskGraph - The opened disk graph. Caller must Close. error - Non-nil if the file doesn't exist, is corrupt, or schema mismatch.
Thread Safety: Safe for concurrent use after construction.
func (*DiskGraph) BuiltAtMilli ¶
BuiltAtMilli returns the build timestamp recorded in metadata.
Thread Safety: Safe for concurrent use.
func (*DiskGraph) Close ¶
Close closes the underlying bbolt database.
Description:
Releases the file lock on the bbolt file. Must be called when done.
Thread Safety: Must not be called concurrently with other methods.
func (*DiskGraph) EdgeCount ¶
EdgeCount returns the number of edges recorded in metadata.
Thread Safety: Safe for concurrent use.
func (*DiskGraph) FileMtimes ¶
FileMtimes returns the file modification times recorded at build time.
Description:
Returns a copy of the file mtimes map to prevent mutation.
Thread Safety: Safe for concurrent use.
func (*DiskGraph) GetNode ¶
GetNode reads a single node from the bbolt file by ID.
Description:
Reads the node from the nodes bucket and its edges from edges_out and edges_in buckets in a single read transaction. Returns a Node with populated Outgoing and Incoming slices, identical to in-memory Graph.
Inputs:
id - The node ID to look up.
Outputs:
*Node - The node with Symbol, Outgoing, and Incoming populated. bool - True if the node was found.
Thread Safety: Safe for concurrent use.
func (*DiskGraph) GraphHash ¶
GraphHash returns the graph hash recorded in metadata.
Thread Safety: Safe for concurrent use.
func (*DiskGraph) LoadAsGraph ¶
LoadAsGraph reconstructs a full in-memory Graph from the bbolt file.
Description:
Iterates all nodes in the nodes bucket and all outgoing edges in the edges_out bucket, calling AddNode and AddEdge to reconstruct the graph with all secondary indexes. Faster than BadgerDB path because: 1. No JSON parsing — binary gob decode 2. No gzip — snappy is faster 3. Sequential B+ tree reads are cache-friendly
Inputs:
ctx - Context for cancellation and tracing. Must not be nil.
Outputs:
*Graph - The reconstructed graph in frozen, read-only state. error - Non-nil if reconstruction fails.
Limitations:
Loads the entire graph into memory. For large graphs, this may require significant RAM. Phase 1b will add direct-from-disk query support.
Thread Safety: Safe for concurrent use on the DiskGraph.
func (*DiskGraph) NodeCount ¶
NodeCount returns the number of nodes recorded in metadata.
Thread Safety: Safe for concurrent use.
func (*DiskGraph) ProjectRoot ¶
ProjectRoot returns the project root recorded in metadata.
Thread Safety: Safe for concurrent use.
type DominanceFrontier ¶
type DominanceFrontier struct {
// Frontier maps each node to its dominance frontier.
// DF(n) contains nodes where n's dominance ends.
Frontier map[string][]string
// MergePoints contains nodes that appear in multiple frontiers.
// These are control flow convergence points.
MergePoints []string
// MergePointCount maps merge point nodeID to count of frontiers it appears in.
// Useful for ranking merge points by convergence degree.
MergePointCount map[string]int
// DomTree is the underlying dominator tree used.
DomTree *DominatorTree
// NodeCount is the number of nodes analyzed.
NodeCount int
// NodesWithFrontiers is the count of nodes with non-empty frontiers.
NodesWithFrontiers int
// TotalFrontierEntries is the sum of all frontier sizes.
TotalFrontierEntries int
// MaxFrontierSize is the largest individual frontier.
MaxFrontierSize int
}
DominanceFrontier contains the computed dominance frontiers.
The dominance frontier DF(n) for a node n contains all nodes y such that:
- n dominates a predecessor of y, but
- n does NOT strictly dominate y
In plain terms: DF(n) = "where n's control ends"
Key concepts:
- Frontier: Maps each node to its dominance frontier nodes
- MergePoints: Nodes appearing in 2+ frontiers (control flow convergence)
- MergePointDegree: How many paths converge at a merge point
Usage:
domTree, _ := analytics.Dominators(ctx, "main")
df, _ := analytics.ComputeDominanceFrontier(ctx, domTree)
// Find where node's control ends
frontier := df.GetFrontier("nodeID")
// Check if a node is a merge point
if df.IsMergePoint("nodeID") {
degree := df.MergePointDegree("nodeID")
}
Thread Safety: Safe for concurrent use after construction.
func (*DominanceFrontier) GetFrontier ¶
func (df *DominanceFrontier) GetFrontier(nodeID string) []string
GetFrontier returns the dominance frontier for a node.
Description:
Returns the set of nodes where the specified node's dominance ends. A node Y is in DF(X) if X dominates a predecessor of Y but does not strictly dominate Y.
Inputs:
- nodeID: The node to query the frontier for. Must be a valid node ID.
Outputs:
- []string: The dominance frontier of the node, or nil if the node has no frontier or the receiver is nil.
Limitations:
- Returns nil for nodes not in the dominator tree.
- The returned slice should not be modified by the caller.
Thread Safety: Safe for concurrent use.
Complexity: O(1) map lookup.
func (*DominanceFrontier) IsMergePoint ¶
func (df *DominanceFrontier) IsMergePoint(nodeID string) bool
IsMergePoint returns true if the node is a merge point.
Description:
A merge point is a node that appears in the dominance frontier of two or more nodes, indicating it is a control flow convergence point where execution from different paths meets.
Inputs:
- nodeID: The node to check. Must be a valid node ID.
Outputs:
- bool: True if the node is in 2+ frontiers, false otherwise.
Limitations:
- Returns false if receiver is nil or nodeID is not in any frontier.
Thread Safety: Safe for concurrent use.
Complexity: O(1) map lookup.
func (*DominanceFrontier) MergePointDegree ¶
func (df *DominanceFrontier) MergePointDegree(nodeID string) int
MergePointDegree returns how many frontiers contain this node.
Description:
Returns the number of distinct dominance frontiers that include this node. A higher degree indicates more control flow paths converge at this point.
Inputs:
- nodeID: The node to query. Must be a valid node ID.
Outputs:
- int: The number of frontiers containing this node. Returns 0 if the node is not in any frontier or the receiver is nil.
Limitations:
- Returns 0 for nodes not appearing in any frontier.
Thread Safety: Safe for concurrent use.
Complexity: O(1) map lookup.
type DominanceFrontierError ¶
type DominanceFrontierError struct {
Message string
}
DominanceFrontierError represents an error in dominance frontier computation.
func (*DominanceFrontierError) Error ¶
func (e *DominanceFrontierError) Error() string
type DominatorError ¶
type DominatorError struct {
Message string
}
DominatorError represents an error in dominator computation.
func (*DominatorError) Error ¶
func (e *DominatorError) Error() string
type DominatorTree ¶
type DominatorTree struct {
// Entry is the entry node ID (root of dominator tree).
Entry string
// ImmediateDom maps nodeID → immediate dominator nodeID.
// The immediate dominator of a node N is the unique node D that:
// 1. Strictly dominates N (D != N)
// 2. Does not strictly dominate any other dominator of N
// Entry node maps to itself.
ImmediateDom map[string]string
// Children maps nodeID → nodes it immediately dominates.
// This is the inverse of ImmediateDom for efficient subtree queries.
// Lazily computed on first access via ensureChildrenBuilt.
Children map[string][]string
// Depth maps nodeID → depth in dominator tree.
// Entry node has depth 0.
Depth map[string]int
// PostOrder contains nodes in reverse postorder for iteration.
// Used internally during computation.
PostOrder []string
// Iterations is the number of convergence iterations.
Iterations int
// Converged indicates whether the algorithm converged before max iterations.
Converged bool
// NodeCount is the total nodes in the graph.
NodeCount int
// EdgeCount is the total edges in the graph.
EdgeCount int
// ReachableCount is the number of nodes reachable from entry.
ReachableCount int
// ExitCount is the number of exit nodes detected (for post-dominators).
// 0 for dominator trees, 1+ for post-dominator trees.
ExitCount int
// UsedVirtualExit indicates whether a virtual exit node was used
// to handle multiple exits (for post-dominators only).
UsedVirtualExit bool
// contains filtered or unexported fields
}
DominatorTree represents the computed dominator relationships.
A dominator tree captures the dominance relationship where node D dominates node N if every path from the entry node to N must go through D.
Thread Safety: Safe for concurrent use after construction.
func (*DominatorTree) DominatedBy ¶
func (dt *DominatorTree) DominatedBy(node string) []string
DominatedBy returns all nodes dominated by a node (subtree rooted at node).
Builds the children map lazily on first call. Returns empty slice if node is not in the dominator tree.
Thread Safety: Safe for concurrent use. Children map is built atomically.
Complexity: O(subtree size) for traversal.
func (*DominatorTree) Dominates ¶
func (dt *DominatorTree) Dominates(a, b string) bool
Dominates returns true if a dominates b.
A node dominates itself (reflexive). Returns false if either node is not in the dominator tree.
Thread Safety: Safe for concurrent use.
Complexity: O(depth) where depth is the depth of b in the dominator tree.
func (*DominatorTree) DominatorsOf ¶
func (dt *DominatorTree) DominatorsOf(node string) []string
DominatorsOf returns all dominators of a node (path from node to entry).
Returns empty slice if node is not in the dominator tree. The result includes the node itself and ends with the entry node.
Thread Safety: Safe for concurrent use.
Complexity: O(depth) where depth is the depth of node.
func (*DominatorTree) LowestCommonDominator ¶
func (dt *DominatorTree) LowestCommonDominator(a, b string) string
LowestCommonDominator finds the LCD of two nodes.
Description:
Finds the deepest node in the dominator tree that dominates both input nodes. Uses an O(depth) algorithm that equalizes depths and walks up until the nodes meet.
Inputs:
- a, b: Node IDs to find common dominator for. Can be the same node.
Outputs:
- string: The LCD node ID. Returns:
- The node itself if a == b
- The entry node if either node is not in the tree
- Empty string if receiver is nil
Example:
lcd := domTree.LowestCommonDominator("saveDB", "sendEmail")
// Returns "validate" (their common mandatory dependency)
Limitations:
- O(depth) per query; use PrepareLCAQueries for many queries
- Nodes not in tree are treated as having entry as LCD
Thread Safety: Safe for concurrent use.
Complexity: O(depth) per query.
func (*DominatorTree) LowestCommonDominatorMultiple ¶
func (dt *DominatorTree) LowestCommonDominatorMultiple(nodes []string) string
LowestCommonDominatorMultiple finds LCD of multiple nodes.
Description:
Iteratively computes LCD for a list of nodes using the property that LCD is associative: LCD(a, b, c) = LCD(LCD(a, b), c).
Inputs:
- nodes: Slice of node IDs. Can be empty or have duplicates.
Outputs:
- string: The LCD of all nodes. Returns:
- Entry node if slice is empty
- The single node if slice has one element
- Empty string if receiver is nil
Example:
lcd := domTree.LowestCommonDominatorMultiple([]string{"saveDB", "sendEmail", "logAction"})
// Returns their common mandatory dependency
Thread Safety: Safe for concurrent use.
Complexity: O(k × depth) where k = len(nodes).
func (*DominatorTree) MaxDepth ¶
func (dt *DominatorTree) MaxDepth() int
MaxDepth returns the maximum depth in the dominator tree.
Thread Safety: Safe for concurrent use.
func (*DominatorTree) PrepareLCAQueries ¶
func (dt *DominatorTree) PrepareLCAQueries() *LCAQueryEngine
PrepareLCAQueries preprocesses for O(1) LCD queries.
Description:
Uses binary lifting to enable O(log depth) LCD queries. The preprocessing builds a sparse table where parent[v][k] = 2^k-th ancestor of v.
Outputs:
- *LCAQueryEngine: Engine for fast LCD queries. Returns nil if receiver is nil.
Example:
lca := domTree.PrepareLCAQueries()
lcd := lca.Query("saveDB", "sendEmail") // O(log depth) instead of O(depth)
Thread Safety: Safe for concurrent use.
Complexity: O(V log V) time and space for preprocessing.
type DominatorTreeCache ¶
type DominatorTreeCache struct {
// contains filtered or unexported fields
}
DominatorTreeCache provides session-level caching for dominator trees.
Description:
Caches a single dominator tree to avoid recomputation when the same entry point is requested multiple times. The cache is automatically invalidated when: - A different entry point is requested - The underlying graph changes (detected via BuiltAtMilli)
Thread Safety:
DominatorTreeCache is safe for concurrent use via RWMutex.
type Edge ¶
type Edge struct {
// FromID is the ID of the source node.
FromID string
// ToID is the ID of the target node.
ToID string
// Type is the relationship type (calls, imports, etc.).
Type EdgeType
// Location is where the relationship is expressed in code.
Location ast.Location
}
Edge represents a directed relationship between two symbols.
Multiple edges of the same type between the same nodes are allowed, representing different call sites or references in the code. For example, if function A calls function B at lines 10 and 20, there will be two EdgeTypeCalls edges with different Locations.
type EdgeError ¶
type EdgeError struct {
// FromID is the source node ID.
FromID string
// ToID is the target node ID.
ToID string
// EdgeType is the type of edge that failed to create.
EdgeType EdgeType
// Err is the underlying error.
Err error
}
EdgeError represents a failure to create a single edge during graph building.
type EdgeType ¶
type EdgeType int
EdgeType defines the type of relationship between symbols.
const ( // EdgeTypeUnknown indicates an unrecognized relationship type. EdgeTypeUnknown EdgeType = iota // EdgeTypeCalls indicates a function/method calls another function/method. EdgeTypeCalls // EdgeTypeImports indicates a file imports a package. EdgeTypeImports // EdgeTypeDefines indicates a file defines a symbol. EdgeTypeDefines // EdgeTypeImplements indicates a type implements an interface. EdgeTypeImplements // EdgeTypeEmbeds indicates a type embeds another type. EdgeTypeEmbeds // EdgeTypeReferences indicates a symbol references another symbol (general). EdgeTypeReferences // EdgeTypeReturns indicates a function returns a type. EdgeTypeReturns // EdgeTypeReceives indicates a method has a receiver of a type. EdgeTypeReceives // EdgeTypeParameters indicates a function takes a type as parameter. EdgeTypeParameters // NumEdgeTypes is the total number of edge types (for array sizing). // GR-08: Used for edgesByType index. NumEdgeTypes )
type EnrichmentStats ¶
type EnrichmentStats struct {
// PlaceholdersQueried is the number of placeholder nodes sent to LSP for resolution.
PlaceholdersQueried int
// PlaceholdersResolved is the number of placeholders successfully resolved to real nodes.
PlaceholdersResolved int
// PlaceholdersFailed is the number of placeholders where LSP returned an error.
PlaceholdersFailed int
// PlaceholdersSkipped is the number of placeholders skipped (wrong language, no location, etc.).
PlaceholdersSkipped int
// FilesQueried is the number of unique source files opened in the LSP server.
FilesQueried int
// OrphanedRemoved is the number of placeholder nodes removed because all their edges were resolved.
OrphanedRemoved int
// LSPErrors is the total number of LSP operation errors (open, definition, etc.).
LSPErrors int
// DurationMicro is the total enrichment phase time in microseconds.
DurationMicro int64
}
EnrichmentStats contains statistics about the LSP enrichment phase.
Description:
GR-74: Tracks how many placeholder nodes were queried against LSP servers, how many were successfully resolved to real symbol nodes, and related metrics.
Thread Safety:
Immutable after the enrichment phase completes.
func (*EnrichmentStats) Merge ¶
func (e *EnrichmentStats) Merge(delta EnrichmentStats)
Merge adds the counters from another EnrichmentStats into this one. DurationMicro and FilesQueried reflect the latest run only (not cumulative).
Description:
GR-76: Used to merge incremental enrichment results into the cumulative stats stored on CachedGraph. Additive for resolution counters so the CRS sees the total enrichment state of the graph, not just the latest delta.
Inputs:
delta - The incremental enrichment stats to merge in.
Thread Safety: NOT safe for concurrent use.
type ExternalDependency ¶
type ExternalDependency struct {
// NodeID is the graph node ID of the external symbol.
// Format: "external:<package>:<name>" or "external::<name>".
NodeID string
// Name is the symbol name (e.g., "run_simple", "read_csv").
Name string
// Package is the inferred external package/module (e.g., "werkzeug.serving", "pandas").
// Empty string if the package could not be determined.
Package string
// CalledFrom is the ID of the internal node that calls this external symbol.
// Empty string if the caller could not be determined from the traversal edges.
CalledFrom string
// Depth is the traversal depth at which this boundary was encountered.
Depth int
}
ExternalDependency represents an external library boundary detected during graph traversal.
Description:
IT-05a: When a traversal tool (get_call_chain, find_callees, find_callers, etc.) encounters a node with Kind == SymbolKindExternal, this struct captures the boundary information so tools can annotate their output.
Thread Safety: This type is safe for concurrent use (immutable after creation).
func ClassifyExternalNodes ¶
func ClassifyExternalNodes(g *Graph, result *TraversalResult) []ExternalDependency
ClassifyExternalNodes identifies external dependency boundaries in a traversal result.
Description:
IT-05a: Iterates over the visited nodes in a TraversalResult, identifies those with Kind == SymbolKindExternal, and returns structured ExternalDependency entries with package information and caller linkage. This function is designed to be called by any traversal tool's buildOutput method. It operates on the already-completed TraversalResult — it does not modify the traversal itself.
Inputs:
- g: The graph containing the nodes. Must not be nil.
- result: The traversal result to classify. Must not be nil.
Outputs:
- []ExternalDependency: External boundaries found, ordered by traversal order. Returns nil if no external nodes are found.
Thread Safety: Safe for concurrent use (reads only).
type FileChange ¶
type FileChange struct {
// Path is the absolute path to the changed file.
Path string
// Op is the type of change.
Op FileOp
// Time is when the change was detected.
Time time.Time
}
FileChange represents a file system change event.
type FileChangeHandler ¶
type FileChangeHandler func(changes []FileChange)
FileChangeHandler is called when debounced changes are ready.
type FileClassification ¶
type FileClassification struct {
// contains filtered or unexported fields
}
FileClassification holds the classification of all files in the graph.
Description:
Stores a binary production/non-production classification for each file based on graph topology (consumption ratio). Non-production files are those that primarily consume production code (test, example, benchmark, documentation code) without being consumed back.
Thread Safety:
FileClassification is safe for concurrent reads after construction. The internal map is never mutated after ClassifyFiles returns.
func ClassifyFiles ¶
func ClassifyFiles(hg *HierarchicalGraph, opts FileClassificationOptions) (*FileClassification, error)
ClassifyFiles classifies all files in the graph as production or non-production using a layered approach: graph topology, language-specific file patterns, test symbol density, and iterative ratio refinement.
Description:
The core insight: test code calls production code, but production code never calls test code. This is measurable as the "consumption ratio": edges_in = cross-file edges pointing INTO this file edges_out = cross-file edges pointing OUT OF this file ratio = edges_in / (edges_in + edges_out) A file that only consumes (test/example) has ratio ≈ 0 (many edges out, few in). A file that only produces (core library) has ratio ≈ 1 (many edges in, few out). However, the raw ratio alone misclassifies test infrastructure files — files like integrationtest_builder.go that are called by many test files. These have high edges_in (from tests calling them) and look like production. The algorithm uses 7 phases to handle this: Phase 1: Load config overrides Phase 2: Group nodes by file Phase 3: Initial consumption ratio (classifies obvious cases) Phase 4: Definitive test file patterns (Go _test.go, Python test_*.py, etc.) Phase 5: Entry point reinforcement + test symbol keyword density Phase 6: Iterative ratio refinement (re-compute using only production edges) Phase 7: Config overrides (user always wins)
Inputs:
hg - The hierarchical graph to classify. Must not be nil and must be frozen. opts - Classification options (project root for config file).
Outputs:
*FileClassification - The classification result. Never nil on success. error - Non-nil if the graph is nil or not frozen.
Thread Safety: Safe for concurrent use (read-only on the graph).
Limitations:
- File-level classification (not directory-level).
- Binary production/non-production (no test vs example vs docs distinction).
- Computed at graph freeze time; does not update dynamically.
Assumptions:
- Graph is frozen and fully indexed before calling.
- Edge topology accurately reflects code dependencies.
func (*FileClassification) IsProduction ¶
func (fc *FileClassification) IsProduction(filePath string) bool
IsProduction returns true if the file is classified as production code.
Description:
O(1) map lookup. Files not present in the classification map are treated as production (conservative default — unknown files are not filtered).
Inputs:
filePath - The file path to check.
Outputs:
bool - True if the file is production code or unknown.
Thread Safety: Safe for concurrent use.
func (*FileClassification) Stats ¶
func (fc *FileClassification) Stats() FileClassificationStats
Stats returns summary statistics about the classification.
Outputs:
FileClassificationStats - Copy of the statistics.
Thread Safety: Safe for concurrent use.
type FileClassificationOptions ¶
type FileClassificationOptions struct {
// ProjectRoot is the absolute path to the project root.
// Used to locate trace.config.yaml for user overrides.
ProjectRoot string
}
FileClassificationOptions configures the file classification algorithm.
type FileClassificationStats ¶
type FileClassificationStats struct {
// TotalFiles is the total number of files classified.
// Invariant: TotalFiles == ProductionFiles + NonProductionFiles.
TotalFiles int
// ProductionFiles is the count of files classified as production.
// Includes IsolatedFiles (isolated files are treated as production).
ProductionFiles int
// NonProductionFiles is the count of files classified as non-production.
NonProductionFiles int
// IsolatedFiles is the count of files with zero cross-file edges.
// These are classified as production (benefit of the doubt).
// IsolatedFiles is a subset of ProductionFiles.
IsolatedFiles int
// LikelyConsumerFiles is a diagnostic counter: files with consumption ratio
// in [0.05, 0.15) that required entry point reinforcement (Phase 4).
// Each is resolved into ProductionFiles or NonProductionFiles.
// This is NOT a separate partition — it overlaps with Prod/NonProd counts.
LikelyConsumerFiles int
}
FileClassificationStats contains summary statistics for logging.
Partition invariant: TotalFiles == ProductionFiles + NonProductionFiles. IsolatedFiles is a subset of ProductionFiles (isolated → production). LikelyConsumerFiles is a diagnostic counter that overlaps with ProductionFiles or NonProductionFiles after Phase 4 resolves them.
type FileError ¶
type FileError struct {
// FilePath is the path to the file that failed.
FilePath string
// Err is the underlying error.
Err error
}
FileError represents a failure to process a single file during graph building.
type FileParseError ¶
FileParseError represents a parse error for a single file.
type FileWatcher ¶
type FileWatcher struct {
// contains filtered or unexported fields
}
FileWatcher watches for file changes with debouncing.
Description ¶
Watches a directory for file changes and batches them using a debounce window. This prevents triggering updates for every keystroke during active editing.
Debouncing ¶
Changes are collected into a buffer. When the debounce period expires without new changes, all collected changes are sent to the handler. This is implemented using channels for efficient, non-blocking operation.
Thread Safety ¶
Safe for concurrent use. The handler is called from a single goroutine.
func NewFileWatcher ¶
func NewFileWatcher(root string, handler FileChangeHandler, opts *FileWatcherOptions) (*FileWatcher, error)
NewFileWatcher creates a new file watcher for the given root directory.
Inputs ¶
- root: Absolute path to the directory to watch.
- handler: Function called with batched changes after debounce.
- opts: Optional configuration (nil uses defaults).
Outputs ¶
- *FileWatcher: Ready-to-use watcher (call Start to begin watching).
- error: Non-nil if the watcher could not be created.
Example ¶
watcher, err := NewFileWatcher("/path/to/project", func(changes []FileChange) {
log.Printf("Changes detected: %d files", len(changes))
// Trigger incremental graph update
}, nil)
if err != nil {
return err
}
defer watcher.Stop()
if err := watcher.Start(ctx); err != nil {
return err
}
func (*FileWatcher) AddPattern ¶
func (w *FileWatcher) AddPattern(pattern string)
AddPattern adds an ignore pattern.
func (*FileWatcher) IsWatching ¶
func (w *FileWatcher) IsWatching() bool
IsWatching returns true if the watcher is currently active.
func (*FileWatcher) SetHandler ¶
func (w *FileWatcher) SetHandler(handler FileChangeHandler)
SetHandler changes the change handler.
func (*FileWatcher) Start ¶
func (w *FileWatcher) Start(ctx context.Context) error
Start begins watching for file changes.
Description ¶
Recursively watches the root directory and all subdirectories. Changes are debounced and sent to the handler in batches.
Inputs ¶
- ctx: Context for cancellation. When canceled, watching stops.
Outputs ¶
- error: Non-nil if watching could not be started.
Behavior ¶
Spawns two goroutines:
- Event processor: Converts fsnotify events to FileChange
- Debouncer: Batches changes and calls handler
Both goroutines exit when Stop() is called or context is canceled.
type FileWatcherOptions ¶
type FileWatcherOptions struct {
// DebounceWindow is how long to wait for more changes before triggering.
// Default: 100ms
DebounceWindow time.Duration
// IgnorePatterns are glob patterns for files/directories to ignore.
// Default: [".git", "node_modules", ".idea", "*.swp", "*.tmp"]
IgnorePatterns []string
// BufferSize is the size of the change buffer channel.
// Default: 1000
BufferSize int
}
FileWatcherOptions configures the FileWatcher.
func DefaultFileWatcherOptions ¶
func DefaultFileWatcherOptions() FileWatcherOptions
DefaultFileWatcherOptions returns sensible defaults.
type Graph ¶
type Graph struct {
// ProjectRoot is the absolute path to the project root directory.
ProjectRoot string
// BuiltAtMilli is the Unix timestamp in milliseconds when Freeze() was called.
// Zero if the graph has not been frozen.
BuiltAtMilli int64
// FileMtimes records file modification times (Unix seconds) at build time.
// CRS-19: Used for staleness detection across sessions. Populated by
// RecordFileMtimes() after Freeze(). Key is relative file path.
FileMtimes map[string]int64
// contains filtered or unexported fields
}
Graph represents the code relationship graph for a project.
Thread Safety:
Graph is NOT safe for concurrent use during building. It is designed for single-writer access during build, then read-only after Freeze(). After Freeze() is called, the graph can be safely read from multiple goroutines, but no further modifications are allowed.
Lifecycle:
- Create with NewGraph(projectRoot)
- Build with AddNode() and AddEdge() calls
- Call Freeze() to finalize
- Query with GetNode(), traversal methods, etc.
func FromSerializable ¶
func FromSerializable(sg *SerializableGraph, opts ...GraphOption) (*Graph, error)
FromSerializable reconstructs a Graph from its serializable representation.
Description:
Creates a new Graph in building state, calls AddNode() and AddEdge() for each entry to correctly build all secondary indexes (nodesByName, nodesByKind, edgesByType, edgesByFile), then calls Freeze(). This reuses the existing construction code path to guarantee index consistency.
Inputs:
sg - The serializable graph to reconstruct. Must not be nil. opts - Optional GraphOption values (e.g., WithMaxNodes).
Outputs:
*Graph - The reconstructed graph in read-only state. error - Non-nil if sg is nil, contains invalid data, or capacity exceeded.
Errors:
Returns error if sg is nil, schema version is unsupported, a node has nil symbol, or AddNode/AddEdge fails.
Complexity:
O(V + E) where V is node count and E is edge count.
Thread Safety:
The returned graph is independent and safe for concurrent reads after construction.
func NewGraph ¶
func NewGraph(projectRoot string, opts ...GraphOption) *Graph
NewGraph creates a new empty graph for the given project root.
Description:
Creates a graph in the Building state, ready to accept AddNode and AddEdge calls. The graph must be frozen with Freeze() before querying.
Inputs:
projectRoot - Absolute path to the project root directory. opts - Optional configuration options.
Example:
// Default options
g := NewGraph("/path/to/project")
// Custom limits
g := NewGraph("/path/to/project",
WithMaxNodes(100_000),
WithMaxEdges(1_000_000),
)
func (*Graph) AddEdge ¶
AddEdge creates a directed edge between two nodes.
Description:
Creates an edge from the source node to the target node with the given type and location. Both nodes must already exist in the graph. Multiple edges of the same type between the same nodes are allowed (representing different call sites or references).
Inputs:
fromID - ID of the source node. toID - ID of the target node. edgeType - The type of relationship. loc - Where the relationship is expressed in code.
Outputs:
error - Non-nil if the graph is frozen, at capacity, or nodes don't exist.
Errors:
ErrGraphFrozen - Graph has been frozen ErrNodeNotFound - Source or target node doesn't exist ErrMaxEdgesExceeded - Graph is at edge capacity
func (*Graph) AddNode ¶
AddNode adds a symbol as a node in the graph.
Description:
Creates a new node from the given symbol and adds it to the graph. The symbol's ID becomes the node's ID.
Inputs:
symbol - The symbol to add. Must not be nil.
Outputs:
*Node - The created node. Can be used to inspect Outgoing/Incoming edges. error - Non-nil if the graph is frozen, at capacity, or symbol is invalid.
Errors:
ErrGraphFrozen - Graph has been frozen ErrInvalidNode - Symbol is nil ErrDuplicateNode - Node with same ID already exists ErrMaxNodesExceeded - Graph is at node capacity
Ownership:
The graph stores a pointer to the symbol but does NOT own it. The symbol MUST NOT be mutated after this call.
func (*Graph) Clone ¶
Clone creates a deep copy of the graph.
Description:
Creates an independent copy of the graph that can be modified without affecting the original. Used for copy-on-write incremental updates.
Outputs:
*Graph - A deep copy of the graph. Always in GraphStateBuilding state
to allow modifications.
Behavior:
- Nodes are deep copied (new Node structs, same Symbol pointers)
- Edges are deep copied (new Edge structs)
- Edge/node references are updated to point to cloned nodes
- BuiltAtMilli is preserved from original
- State is reset to GraphStateBuilding
Thread Safety:
The returned graph is independent and can be modified without synchronization.
func (*Graph) Edges ¶
Edges returns a slice of all edges in the graph.
Description:
Returns the internal edge slice. Callers should NOT modify the returned slice.
func (*Graph) FindCalleesByID ¶
func (g *Graph) FindCalleesByID(ctx context.Context, symbolID string, opts ...QueryOption) (*QueryResult, error)
FindCalleesByID returns all symbols called by the given function/method.
Description:
Finds all functions/methods that the source has CALLS edges to. Uses symbol ID for unambiguous lookup.
Inputs:
ctx - Context for cancellation symbolID - ID of the function/method to find callees for opts - Query options (Limit, Timeout)
Outputs:
*QueryResult - Symbols called by the source (empty if none), with metadata error - Non-nil if context error occurs
func (*Graph) FindCalleesByName ¶
func (g *Graph) FindCalleesByName(ctx context.Context, name string, opts ...QueryOption) (map[string]*QueryResult, error)
FindCalleesByName returns callees for all symbols matching the given name.
Description:
When multiple symbols have the same name, this returns callees for each, keyed by symbol ID.
Inputs:
ctx - Context for cancellation name - Symbol name to search for opts - Query options (Limit per symbol, Timeout)
Outputs:
map[string]*QueryResult - Symbol ID → callees of that symbol error - Non-nil if context error occurs
func (*Graph) FindCallersByID ¶
func (g *Graph) FindCallersByID(ctx context.Context, symbolID string, opts ...QueryOption) (*QueryResult, error)
FindCallersByID returns all symbols that call the given function/method.
Description:
Finds all functions/methods that have a CALLS edge to the target. Uses symbol ID for unambiguous lookup.
Inputs:
ctx - Context for cancellation symbolID - ID of the function/method to find callers for opts - Query options (Limit, Timeout)
Outputs:
*QueryResult - Symbols that call the target (empty if none), with metadata error - Non-nil if context error occurs
Limitations:
Only finds direct callers (not transitive) May miss callers through function pointers/interfaces
func (*Graph) FindCallersByName ¶
func (g *Graph) FindCallersByName(ctx context.Context, name string, opts ...QueryOption) (map[string]*QueryResult, error)
FindCallersByName returns callers for all symbols matching the given name.
Description:
When multiple symbols have the same name (e.g., Setup in different packages), this returns callers for each, keyed by symbol ID.
Inputs:
ctx - Context for cancellation name - Symbol name to search for opts - Query options (Limit per symbol, Timeout)
Outputs:
map[string]*QueryResult - Symbol ID → callers of that symbol error - Non-nil if context error occurs
func (*Graph) FindCallersWithInheritance ¶
func (g *Graph) FindCallersWithInheritance(ctx context.Context, symbolID string, parentMethodIDs []string, opts ...QueryOption) (*InheritanceQueryResult, error)
FindCallersWithInheritance returns callers of a method AND callers of the same method on parent classes in the inheritance chain.
Description:
When a child class overrides a parent method, calls through the parent class (e.g., this.renderImmediately() inside Component) create edges to the parent's method, not the child's. This function collects callers from the entire inheritance chain so that callers of both Plot.renderImmediately AND Component.renderImmediately are returned.
Inputs:
ctx - Context for cancellation. symbolID - ID of the target method (e.g., Plot.renderImmediately). parentMethodIDs - IDs of the same-named method on parent classes. opts - Query options (Limit, Timeout).
Outputs:
*QueryResult - Combined callers from all inheritance levels, deduplicated by ID. error - Non-nil if context error occurs.
func (*Graph) FindImplementationsByID ¶
func (g *Graph) FindImplementationsByID(ctx context.Context, interfaceID string, opts ...QueryOption) (*QueryResult, error)
FindImplementationsByID returns all types that implement or extend the given type.
Description:
Finds all types that have an IMPLEMENTS or EMBEDS edge to the target. IMPLEMENTS edges come from Go interface satisfaction and TS implements clauses. EMBEDS edges come from Python/JS/TS class extends and Go struct embedding. Uses target ID for unambiguous lookup. Deduplicates results when a type has both edge types to the same target.
Inputs:
ctx - Context for cancellation interfaceID - ID of the interface, class, or struct to find implementers for opts - Query options (Limit, Timeout)
Outputs:
*QueryResult - Types implementing/extending the target (empty if none) error - Non-nil if context error occurs
func (*Graph) FindImplementationsByName ¶
func (g *Graph) FindImplementationsByName(ctx context.Context, name string, opts ...QueryOption) (map[string]*QueryResult, error)
FindImplementationsByName returns implementations for all types matching the given name.
Description:
When multiple types (interfaces, classes, structs) share the same name, this returns implementers/subclasses for each, keyed by type ID. Non-type symbols (functions, variables, etc.) are filtered out.
Inputs:
ctx - Context for cancellation name - Interface/class/struct name to search for opts - Query options (Limit per target type, Timeout)
Outputs:
map[string]*QueryResult - Type ID → implementers/subclasses of that type error - Non-nil if context error occurs
func (*Graph) FindImporters ¶
func (g *Graph) FindImporters(ctx context.Context, packagePath string, opts ...QueryOption) ([]string, error)
FindImporters returns all file paths that import the given package.
Description:
Finds all files that have an IMPORTS edge to nodes in the given package.
Inputs:
ctx - Context for cancellation packagePath - Package path to find importers for opts - Query options (Limit, Timeout)
Outputs:
[]string - File paths that import the package error - Non-nil if context error occurs
func (*Graph) FindReferencesByID ¶
func (g *Graph) FindReferencesByID(ctx context.Context, symbolID string, opts ...QueryOption) ([]ast.Location, error)
FindReferencesByID returns all locations where the given symbol is referenced.
Description:
Finds all incoming edges to the symbol and returns their locations. This includes calls, type references, etc.
Inputs:
ctx - Context for cancellation symbolID - ID of the symbol to find references for opts - Query options (Limit, Timeout)
Outputs:
[]ast.Location - Locations where the symbol is referenced error - Non-nil if context error occurs
func (*Graph) Freeze ¶
func (g *Graph) Freeze()
Freeze transitions the graph to read-only mode.
Description:
After calling Freeze(), AddNode and AddEdge will return ErrGraphFrozen. This operation is irreversible. The BuiltAtMilli timestamp is set to the current time. Validates secondary index integrity before freezing.
Thread Safety:
After Freeze() returns, the graph can be safely read from multiple goroutines concurrently.
Limitations:
Index validation adds O(V + E) overhead on freeze. For graphs with millions of nodes, consider using FreezeWithoutValidation().
func (*Graph) GetCallGraph ¶
func (g *Graph) GetCallGraph(ctx context.Context, symbolID string, opts ...QueryOption) (*TraversalResult, error)
GetCallGraph returns the call tree rooted at a function.
Description:
Performs iterative BFS traversal following CALLS edges up to maxDepth. Uses iterative approach (not recursive) to handle deep graphs without stack overflow.
Inputs:
ctx - Context for cancellation (checked every 100 nodes) symbolID - Root function ID (must be unambiguous) opts - Query options including MaxDepth (default: 10, max: 100)
Outputs:
*TraversalResult - Visited nodes and edges, with Truncated flag error - Non-nil if root not found
func (*Graph) GetCallGraphParallel ¶
func (g *Graph) GetCallGraphParallel(ctx context.Context, symbolID string, opts ...QueryOption) (*TraversalResult, error)
GetCallGraphParallel returns the call tree using parallel BFS for wide graphs.
Description:
Performs level-synchronous parallel BFS traversal. Automatically chooses parallel or sequential mode based on level width (threshold: 32 nodes). For narrow graphs, falls back to sequential for better cache locality.
Inputs:
- ctx: Context for cancellation. Must not be nil.
- symbolID: Root function ID. Must exist in graph.
- opts: Query options (MaxDepth, Limit).
Outputs:
- *TraversalResult: Visited nodes (order may vary in parallel mode) and edges.
- error: Non-nil if root not found.
Performance:
| Graph Type | Complexity | |---------------|-------------------------------| | Sequential | O(V + E) | | Parallel wide | O((V + E) / P + D * barrier) | where P = min(level_size, NumCPU, 8), D = depth
Limitations:
- VisitedNodes order is non-deterministic when parallel mode is used
- Parallel mode only engaged for levels with >32 nodes
- Memory overhead: ~O(level_size) per level for work distribution
Thread Safety: Safe for concurrent use.
func (*Graph) GetDependencyTree ¶
func (g *Graph) GetDependencyTree(ctx context.Context, filePath string, opts ...QueryOption) (*TraversalResult, error)
GetDependencyTree returns the dependency tree for a file.
Description:
Performs iterative BFS traversal following IMPORTS edges up to maxDepth. Returns all transitive dependencies.
Inputs:
ctx - Context for cancellation (checked every 100 nodes) filePath - File path to find dependencies for opts - Query options including MaxDepth (default: 10, max: 100)
Outputs:
*TraversalResult - Visited nodes and edges, with Truncated flag error - Non-nil if file not found in graph
func (*Graph) GetEdgeCountByFile ¶
GetEdgeCountByFile returns the count of edges with Location in the given file.
Description:
GR-09: Uses secondary index for O(1) count without copying. More efficient than len(GetEdgesByFile()) for just getting counts.
Inputs:
filePath - The file path to count edges for.
Outputs:
int - Number of edges with Location in that file. Zero if none found.
Complexity:
O(1) - Direct map access and len().
Thread Safety:
Safe for concurrent use on frozen graphs.
func (*Graph) GetEdgeCountByType ¶
GetEdgeCountByType returns the count of edges of the given type.
Description:
GR-08: Uses secondary index for O(1) count without copying. More efficient than len(GetEdgesByType()) for just getting counts.
Inputs:
edgeType - The edge type to count (e.g., EdgeTypeCalls).
Outputs:
int - Number of edges of that type. Zero if invalid type or none found.
Complexity:
O(1) - Direct array access and len().
Thread Safety:
Safe for concurrent use on frozen graphs.
Example:
callCount := g.GetEdgeCountByType(EdgeTypeCalls)
func (*Graph) GetEdgesByFile ¶
GetEdgesByFile returns all edges with Location in the given file.
Description:
GR-09: Uses secondary index for O(1) lookup. Returns a defensive copy to prevent external mutation. Note: Indexes by edge.Location.FilePath (where the edge is expressed), which may differ from the source node's file path.
Inputs:
filePath - The file path to filter by.
Outputs:
[]*Edge - Edges with Location in that file. Empty slice if none found.
Complexity:
O(1) lookup + O(k) copy where k = edges in that file.
Thread Safety:
Safe for concurrent use on frozen graphs.
Example:
edges := g.GetEdgesByFile("pkg/handler.go")
fmt.Printf("Found %d edges in handler.go\n", len(edges))
func (*Graph) GetEdgesByType ¶
GetEdgesByType returns all edges of the given type.
Description:
GR-08: Uses secondary index for O(1) lookup. Returns a defensive copy to prevent external mutation.
Inputs:
edgeType - The edge type to filter by (e.g., EdgeTypeCalls).
Outputs:
[]*Edge - Edges of that type. Empty slice if none found or invalid type.
Complexity:
O(1) lookup + O(k) copy where k = edges of that type.
Thread Safety:
Safe for concurrent use on frozen graphs.
func (*Graph) GetNode ¶
GetNode retrieves a node by its ID.
Description:
Performs O(1) lookup in the node map.
Inputs:
id - The node ID (same as Symbol.ID).
Outputs:
*Node - The node if found, nil otherwise. bool - True if the node was found.
func (*Graph) GetNodeCountByKind ¶
func (g *Graph) GetNodeCountByKind(kind ast.SymbolKind) int
GetNodeCountByKind returns the count of nodes of the given kind.
Description:
GR-07: Uses secondary index for O(1) count without copying. More efficient than len(GetNodesByKind()) for just getting counts.
Inputs:
kind - The symbol kind to count.
Outputs:
int - Number of nodes of that kind. Zero if none found.
Complexity:
O(1) - Direct map access and len().
Thread Safety:
Safe for concurrent use on frozen graphs.
func (*Graph) GetNodeCountByName ¶
GetNodeCountByName returns the count of nodes with the given name.
Description:
GR-06: Uses secondary index for O(1) count without copying. More efficient than len(GetNodesByName()) for just getting counts.
Inputs:
name - The symbol name to count.
Outputs:
int - Number of nodes with that name. Zero if none found.
Complexity:
O(1) - Direct map access and len().
Thread Safety:
Safe for concurrent use on frozen graphs.
func (*Graph) GetNodesByFile ¶
GetNodesByFile returns all nodes from a specific file.
Description:
Returns all nodes where the Symbol's FilePath matches the given path. Useful for identifying what symbols are defined in a file.
Inputs:
filePath - The relative file path to search for.
Outputs:
[]*Node - Nodes from that file. Empty slice if none found.
func (*Graph) GetNodesByKind ¶
func (g *Graph) GetNodesByKind(kind ast.SymbolKind) []*Node
GetNodesByKind returns all nodes of the given symbol kind.
Description:
GR-07: Uses secondary index for O(1) lookup. Returns a defensive copy to prevent external mutation.
Inputs:
kind - The symbol kind to filter by (e.g., SymbolKindFunction).
Outputs:
[]*Node - Nodes of that kind. Empty slice if none found.
Complexity:
O(1) lookup + O(k) copy where k = nodes of that kind.
Thread Safety:
Safe for concurrent use on frozen graphs.
func (*Graph) GetNodesByName ¶
GetNodesByName returns all nodes with the given symbol name.
Description:
GR-06: Uses secondary index for O(1) lookup. Multiple symbols can share a name (e.g., "Setup" in different packages). Returns a defensive copy to prevent external mutation.
Inputs:
name - The symbol name to search for.
Outputs:
[]*Node - Nodes with that name. Empty slice if none found.
Complexity:
O(1) lookup + O(k) copy where k = nodes with that name.
Thread Safety:
Safe for concurrent use on frozen graphs.
func (*Graph) GetReverseCallGraph ¶
func (g *Graph) GetReverseCallGraph(ctx context.Context, symbolID string, opts ...QueryOption) (*TraversalResult, error)
GetReverseCallGraph returns the callers tree rooted at a function.
Description:
Performs iterative BFS traversal following CALLS edges backwards (finding callers) up to maxDepth.
Inputs:
ctx - Context for cancellation (checked every 100 nodes) symbolID - Root function ID (must be unambiguous) opts - Query options including MaxDepth (default: 10, max: 100)
Outputs:
*TraversalResult - Visited nodes and edges, with Truncated flag error - Non-nil if root not found
func (*Graph) GetReverseCallGraphParallel ¶
func (g *Graph) GetReverseCallGraphParallel(ctx context.Context, symbolID string, opts ...QueryOption) (*TraversalResult, error)
GetReverseCallGraphParallel returns the callers tree using parallel BFS.
Description:
Performs level-synchronous parallel BFS traversal following CALLS edges backwards (finding callers). Automatically chooses parallel or sequential mode based on level width.
Inputs:
- ctx: Context for cancellation. Must not be nil.
- symbolID: Root function ID. Must exist in graph.
- opts: Query options (MaxDepth, Limit).
Outputs:
- *TraversalResult: Caller nodes (order may vary in parallel mode) and edges.
- error: Non-nil if root not found.
Limitations:
- VisitedNodes order is non-deterministic when parallel mode is used
- Parallel mode only engaged for levels with >32 nodes
Thread Safety: Safe for concurrent use.
func (*Graph) GetTypeHierarchy ¶
func (g *Graph) GetTypeHierarchy(ctx context.Context, typeID string, opts ...QueryOption) (*TraversalResult, error)
GetTypeHierarchy returns the type hierarchy for a type.
Description:
Performs iterative BFS traversal following IMPLEMENTS and EMBEDS edges. Returns all interfaces implemented and types embedded.
Inputs:
ctx - Context for cancellation (checked every 100 nodes) typeID - Type ID to find hierarchy for opts - Query options including MaxDepth (default: 10, max: 100)
Outputs:
*TraversalResult - Visited nodes and edges, with Truncated flag error - Non-nil if type not found
func (*Graph) Hash ¶
Hash returns a deterministic hash of the graph structure.
Description:
Computes a hash over all nodes and edges in the graph. The hash is deterministic (same graph structure always produces same hash) and can be used to detect graph changes for cache invalidation. The hash includes: - All node IDs (sorted) - All edge tuples (from, to, type) sorted Uses FNV-1a hash for speed and good distribution.
Outputs:
- string: Hex-encoded hash (16 characters). Empty string if graph is nil.
Example:
hash1 := graph.Hash()
// ... modify graph ...
hash2 := graph.Hash()
if hash1 != hash2 {
log.Info("graph changed, invalidating caches")
}
Complexity:
O(V log V + E log E) - Sorting nodes and edges O(V + E) if already sorted (frozen graphs)
Thread Safety:
Safe for concurrent use on frozen graphs.
func (*Graph) IsTree ¶
IsTree validates that the graph forms a valid tree structure.
Description:
Checks that graph is acyclic, connected, and has exactly V-1 edges. Uses DFS to detect cycles and verify connectivity from root.
Complexity: O(V + E)
Thread Safety: Safe for concurrent use (read-only on frozen graph).
func (*Graph) MaterializeToDisk ¶
MaterializeToDisk persists a frozen graph to a bbolt file.
Description:
Writes all nodes, edges, secondary indexes, and metadata to a bbolt file in a single atomic transaction. The graph must be frozen (read-only). If a file already exists at path, it is replaced atomically via write-to-temp + rename.
Inputs:
ctx - Context for cancellation and tracing. Must not be nil. path - Absolute path to the bbolt file to create. Parent dir must exist.
Outputs:
error - Non-nil if the graph is not frozen, encoding fails, or I/O fails.
Limitations:
Requires the graph to be frozen. Does not compress the bbolt file itself (individual values are snappy-compressed).
Thread Safety:
Safe for concurrent use on frozen graphs (read-only access to graph data).
func (*Graph) MergeParseResult ¶
func (g *Graph) MergeParseResult(result *ast.ParseResult) (int, error)
MergeParseResult adds nodes and edges from a ParseResult.
Description:
Adds all symbols from the ParseResult as nodes and creates edges based on import relationships. Used for incremental updates when adding newly parsed files.
Inputs:
result - The ParseResult containing symbols to add.
Outputs:
int - Number of nodes added. error - Non-nil if the graph is frozen or capacity exceeded.
Errors:
ErrGraphFrozen - Graph has been frozen ErrMaxNodesExceeded - Graph is at node capacity ErrMaxEdgesExceeded - Graph is at edge capacity
Behavior:
- Adds all top-level symbols as nodes
- Creates EdgeTypeDefines edges from file node to symbols
- Skips duplicate nodes (by ID)
- Does NOT add children (caller should flatten if needed)
Thread Safety:
NOT safe for concurrent use during modification.
func (*Graph) Nodes ¶
Nodes returns an iterator function over all nodes in the graph.
Description:
Returns a function that can be used to iterate over all nodes. This allows iteration without exposing the internal map.
Example:
for id, node := range g.Nodes() {
fmt.Printf("Node: %s\n", id)
}
func (*Graph) RemoveFile ¶
RemoveFile removes all nodes and edges associated with a file.
Description:
Removes all symbols (nodes) that were defined in the specified file, along with all edges that reference those nodes. Used for incremental updates when a file is deleted or modified.
Inputs:
filePath - The relative file path to remove (must match Symbol.FilePath).
Outputs:
int - Number of nodes removed. error - Non-nil if the graph is frozen.
Errors:
ErrGraphFrozen - Graph has been frozen
Behavior:
- Removes all nodes where Symbol.FilePath matches
- Removes all edges where FromID or ToID references removed nodes
- Updates Incoming/Outgoing slices of remaining nodes
Thread Safety:
NOT safe for concurrent use during modification.
func (*Graph) RemoveNode ¶
RemoveNode removes a node and all its edges from the graph.
Description:
GR-74: Used by LSP enrichment to clean up orphaned placeholder nodes after all their edges have been resolved to real targets. Removes the node from all indexes (nodes, nodesByName, nodesByKind) and removes all edges connected to the node from the graph's edges slice and secondary indexes.
Inputs:
id - The ID of the node to remove. Must exist in the graph.
Outputs:
error - Non-nil if graph is frozen or node not found.
Errors:
ErrGraphFrozen - Graph has been frozen ErrNodeNotFound - Node does not exist
Thread Safety:
NOT safe for concurrent use. Must be called during build phase only.
func (*Graph) ReplaceEdgeTarget ¶
ReplaceEdgeTarget re-points an existing edge from its current target to a new target node.
Description:
GR-74: Used by LSP enrichment to replace placeholder edge targets with real symbol nodes after LSP definition lookup resolves the placeholder. Removes the edge from the old target's Incoming slice, updates edge.ToID, and appends to the new target's Incoming slice.
Inputs:
edge - The edge to retarget. Must not be nil. newToID - ID of the new target node. Must exist in the graph.
Outputs:
error - Non-nil if graph is frozen, edge is nil, or newToID not found.
Errors:
ErrGraphFrozen - Graph has been frozen ErrNodeNotFound - newToID does not exist in the graph
Thread Safety:
NOT safe for concurrent use. Must be called during build phase only.
func (*Graph) ShortestPath ¶
ShortestPath finds the shortest path between two symbols.
Description:
Uses BFS to find minimum-edge path. Considers all edge types. Returns immediately if fromID == toID (path of length 0).
Inputs:
ctx - Context for cancellation fromID - Starting node ID toID - Target node ID
Outputs:
*PathResult - Path details or empty if no path exists error - Non-nil if nodes not found
func (*Graph) State ¶
func (g *Graph) State() GraphState
State returns the current lifecycle state of the graph.
func (*Graph) Stats ¶
func (g *Graph) Stats() GraphStats
Stats returns statistics about the graph.
Description:
Returns statistics including node/edge counts, breakdowns by edge type and symbol kind, and capacity information. Uses secondary indexes for O(1) lookups instead of O(V+E) iteration.
Outputs:
GraphStats - Statistics about the graph.
Complexity:
O(K + T) where K is number of symbol kinds and T is number of edge types. GR-06/07/08: Improved from O(V + E) via secondary indexes.
Thread Safety:
Safe for concurrent use on frozen graphs. Not safe during building.
func (*Graph) ToSerializable ¶
func (g *Graph) ToSerializable() *SerializableGraph
ToSerializable converts a Graph to its JSON-serializable representation.
Description:
Iterates all nodes (sorted by ID for deterministic output) and all edges to produce a SerializableGraph suitable for JSON encoding. The resulting structure contains all data needed to reconstruct the graph.
Outputs:
*SerializableGraph - The serializable representation. Never nil.
Complexity:
O(V log V + E) where V is node count and E is edge count. Sorting nodes by ID dominates.
Thread Safety:
Safe for concurrent use on frozen graphs.
func (*Graph) Validate ¶
Validate checks that the graph is in a consistent state for querying.
Description:
Verifies all edges reference existing nodes. Should be called once after build, before queries. Queries will return error if validation fails.
Outputs:
error - Non-nil if graph is corrupt (dangling edges)
Example:
if err := graph.Validate(); err != nil {
return fmt.Errorf("graph corrupt: %w", err)
}
type GraphAnalytics ¶
type GraphAnalytics struct {
// contains filtered or unexported fields
}
GraphAnalytics provides analytical queries over the code graph.
Description:
GraphAnalytics operates on a HierarchicalGraph to provide insights about code structure, including: - Hotspot detection (most-connected nodes) - Dead code detection (unreachable symbols) - Cyclic dependency detection (using Tarjan's SCC) - Package coupling metrics - HLD query wrappers with CRS integration
Thread Safety:
GraphAnalytics is safe for concurrent use (read-only queries). Session management is protected by RWMutex.
Performance:
| Operation | Complexity | |-----------|------------| | HotSpots | O(V log k) for top-k | | DeadCode | O(V) | | CyclicDependencies | O(V + E) | | PackageCoupling | O(P) where P = packages | | HLD Queries | O(log V) |
func NewGraphAnalytics ¶
func NewGraphAnalytics(graph *HierarchicalGraph) *GraphAnalytics
NewGraphAnalytics creates a new analytics instance for the given graph.
Inputs:
graph - The hierarchical graph to analyze. Must not be nil.
Outputs:
*GraphAnalytics - The analytics instance. Never nil if graph is valid.
func NewGraphAnalyticsWithCRS ¶
func NewGraphAnalyticsWithCRS(graph *HierarchicalGraph, hld *HLDecomposition, crsInstance crs.CRS, opts *GraphAnalyticsOptions) *GraphAnalytics
NewGraphAnalyticsWithCRS creates an analytics instance with CRS integration.
Description:
Creates a GraphAnalytics instance that records HLD query operations to CRS. Use this when you want query tracing and replay capabilities.
Inputs:
- graph: The hierarchical graph to analyze. Must not be nil.
- hld: The HLD instance for LCA queries. Must not be nil.
- crsInstance: The CRS instance for recording. Must not be nil.
- opts: Configuration options. If nil, uses defaults (no auto-session).
Outputs:
- *GraphAnalytics: The analytics instance with CRS enabled. Never nil if inputs valid.
Thread Safety: Safe for concurrent use.
func (*GraphAnalytics) ArticulationPoints ¶
func (a *GraphAnalytics) ArticulationPoints(ctx context.Context) (*ArticulationResult, error)
ArticulationPoints finds cut vertices using Tarjan's algorithm.
Description:
Uses iterative DFS (to avoid stack overflow on deep graphs) to find nodes whose removal would disconnect the graph when treated as undirected. Also identifies bridges (critical edges). The algorithm treats the directed call graph as undirected, which means both incoming and outgoing edges are considered for connectivity analysis.
Inputs:
- ctx: Context for cancellation. Must not be nil. Checked every 1000 nodes.
Outputs:
- *ArticulationResult: Analysis results. Never nil on success. Contains Points (cut vertices), Bridges (critical edges), and component count.
- error: Non-nil only on context cancellation. Partial results still returned.
Example:
result, err := analytics.ArticulationPoints(ctx)
if err != nil {
// Context was cancelled - result may be partial
log.Printf("cancelled after processing %d nodes", result.NodeCount)
}
for _, point := range result.Points {
log.Printf("Single point of failure: %s", point)
}
Limitations:
- Treats directed graph as undirected for connectivity
- Does not identify direction-sensitive articulation points
- Memory usage scales with O(V) for auxiliary data structures
Assumptions:
- Graph is frozen (guaranteed by HierarchicalGraph construction)
- Node IDs are valid and non-empty strings
- Self-loops are skipped (do not affect connectivity)
Thread Safety: Safe for concurrent use (read-only on graph).
Complexity: O(V + E) time, O(V) space.
func (*GraphAnalytics) ArticulationPointsWithCRS ¶
func (a *GraphAnalytics) ArticulationPointsWithCRS(ctx context.Context) (*ArticulationResult, crs.TraceStep)
ArticulationPointsWithCRS wraps ArticulationPoints with CRS tracing.
Description:
Provides the same functionality as ArticulationPoints but also returns a TraceStep for recording in the Code Reasoning State (CRS).
Thread Safety: Safe for concurrent use.
func (*GraphAnalytics) BatchLCAWithCRS ¶
func (ga *GraphAnalytics) BatchLCAWithCRS(ctx context.Context, pairs [][2]string) ([]string, []error, error)
BatchLCAWithCRS computes batch LCA and records a StepRecord to CRS.
Description:
Wraps HLD.BatchLCA() to provide CRS integration. Records a single StepRecord for the entire batch operation with aggregate statistics.
Inputs:
- ctx: Context for cancellation and tracing. Must not be nil.
- pairs: Array of node ID pairs. Must not be empty.
Outputs:
- []string: LCA results (one per pair).
- []error: Errors (one per pair, nil on success).
- error: Non-nil on total failure.
Thread Safety: Safe for concurrent use.
func (*GraphAnalytics) CheckReducibility ¶
func (a *GraphAnalytics) CheckReducibility(ctx context.Context, domTree *DominatorTree) (*ReducibilityResult, error)
CheckReducibility analyzes graph reducibility using dominator tree.
Description:
Determines if the graph is reducible by classifying edges relative to the dominator tree. A graph is reducible if and only if all back edges go to dominators (natural loops only). Identifies and enumerates irreducible regions - subgraphs with multiple entry points that indicate complex control flow.
Inputs:
- ctx: Context for cancellation. Must not be nil.
- domTree: Pre-computed dominator tree. Must not be nil.
Outputs:
- *ReducibilityResult: Analysis results. Never nil on success.
- error: Non-nil on failure with context-wrapped message.
Algorithm:
- For each edge (u, v) in the graph: - If domTree.Dominates(v, u): back edge (OK) - If domTree.Dominates(u, v): forward edge (OK) - Otherwise: cross edge (potentially irreducible)
- For each cross edge, find the irreducible region it creates
- Deduplicate overlapping regions
- Compute reducibility score
Complexity:
- Time: O(E × depth) for edge classification via dominator queries
- Time: O(V + E) for region enumeration per irreducible region
- Space: O(V) for tracking node membership
Limitations:
- Maximum 100 irreducible regions enumerated
- Disconnected nodes (unreachable from entry) are not analyzed
Assumptions:
- domTree was computed from the same graph
- Graph is frozen before analysis
Thread Safety: Safe for concurrent use (read-only on graph/domTree).
func (*GraphAnalytics) CheckReducibilityWithCRS ¶
func (a *GraphAnalytics) CheckReducibilityWithCRS( ctx context.Context, domTree *DominatorTree, ) (*ReducibilityResult, crs.TraceStep)
CheckReducibilityWithCRS wraps CheckReducibility with CRS tracing.
Description:
Provides the same functionality as CheckReducibility but also returns a TraceStep for recording in the Code Reasoning State (CRS).
Thread Safety: Safe for concurrent use.
func (*GraphAnalytics) ComputeControlDependence ¶
func (a *GraphAnalytics) ComputeControlDependence( ctx context.Context, postDomTree *DominatorTree, ) (*ControlDependence, error)
ComputeControlDependence builds the control dependence graph.
Description:
Uses the post-dominance frontier to compute which nodes control the execution of other nodes. A decision point controls nodes that appear in its post-dominance frontier. For each node n in PostDF(m), n is control-dependent on m.
Inputs:
- ctx: Context for cancellation. Checked during dominance frontier computation.
- postDomTree: Pre-computed post-dominator tree. Must not be nil.
Outputs:
- *ControlDependence: The control dependence relationships. Never nil.
- error: Non-nil if postDomTree is nil or context cancelled.
Example:
postDomTree, _ := analytics.PostDominators(ctx, "")
cd, err := analytics.ComputeControlDependence(ctx, postDomTree)
if err != nil {
log.Fatal(err)
}
// What controls the execution of handler?
controllers := cd.GetDependencies("handler")
// What does the condition control?
controlled := cd.GetDependents("condition")
Limitations:
- Only computes dependencies for nodes in the post-dominator tree
- Unreachable nodes have no dependencies
- Memory: O(E) where E is control dependence edges
Assumptions:
- postDomTree is valid and consistent
- Graph is frozen
Thread Safety: Safe for concurrent use (read-only on graph and postDomTree).
Complexity: O(E × depth), same as dominance frontier.
func (*GraphAnalytics) ComputeControlDependenceWithCRS ¶
func (a *GraphAnalytics) ComputeControlDependenceWithCRS( ctx context.Context, postDomTree *DominatorTree, ) (*ControlDependence, crs.TraceStep)
ComputeControlDependenceWithCRS wraps ComputeControlDependence with CRS tracing.
Description:
Provides the same functionality as ComputeControlDependence but also returns a TraceStep for recording in the Code Reasoning State (CRS).
Thread Safety: Safe for concurrent use.
func (*GraphAnalytics) ComputeDominanceFrontier ¶
func (a *GraphAnalytics) ComputeDominanceFrontier( ctx context.Context, domTree *DominatorTree, ) (*DominanceFrontier, error)
ComputeDominanceFrontier computes dominance frontiers for all nodes.
Description:
For each node n, DF(n) contains nodes where n's dominance ends. Uses the standard algorithm from Cytron et al. (1991). A node m is in DF(n) iff: 1. n dominates a predecessor of m 2. n does NOT strictly dominate m
Inputs:
- ctx: Context for cancellation. Checked every 500 nodes.
- domTree: Pre-computed dominator tree. Must not be nil.
Outputs:
- *DominanceFrontier: Frontiers for all nodes. Never nil.
- error: Non-nil if domTree is nil or context cancelled.
Example:
domTree, _ := analytics.Dominators(ctx, "main")
df, err := analytics.ComputeDominanceFrontier(ctx, domTree)
if err != nil {
log.Fatal(err)
}
mergePoints := df.MergePoints
Limitations:
- Only computes frontiers for nodes in the dominator tree
- Unreachable nodes have no frontier
- Memory: O(V) typical, O(V²) worst case
Assumptions:
- domTree is valid and consistent
- Graph is frozen
Thread Safety: Safe for concurrent use (read-only on graph and domTree).
Complexity: O(E × depth), typically O(E) for shallow trees.
func (*GraphAnalytics) ComputeDominanceFrontierWithCRS ¶
func (a *GraphAnalytics) ComputeDominanceFrontierWithCRS( ctx context.Context, domTree *DominatorTree, ) (*DominanceFrontier, crs.TraceStep)
ComputeDominanceFrontierWithCRS wraps ComputeDominanceFrontier with CRS tracing.
Description:
Provides the same functionality as ComputeDominanceFrontier but also returns a TraceStep for recording in the Code Reasoning State (CRS).
Thread Safety: Safe for concurrent use.
func (*GraphAnalytics) CyclicDependencies ¶
func (a *GraphAnalytics) CyclicDependencies() []CyclicDependency
CyclicDependencies finds all cycles in the graph using Tarjan's SCC algorithm.
Description:
Uses Tarjan's strongly connected components algorithm to find cycles. Only returns components with more than one node (actual cycles). Time complexity: O(V + E) Space complexity: O(V) Implementation uses an explicit call stack to avoid stack overflow on deep graphs (CR-2 fix).
Outputs:
[]CyclicDependency - All cycles found, sorted by length descending.
Thread Safety: Safe for concurrent use.
func (*GraphAnalytics) CyclicDependenciesWithCRS ¶
func (a *GraphAnalytics) CyclicDependenciesWithCRS(ctx context.Context) ([]CyclicDependency, crs.TraceStep)
CyclicDependenciesWithCRS returns cycles with a TraceStep.
Thread Safety: Safe for concurrent use.
func (*GraphAnalytics) DeadCode ¶
func (a *GraphAnalytics) DeadCode() []DeadCodeNode
DeadCode finds symbols with no incoming edges (potential unused code).
Description:
Returns nodes that have no callers/references, excluding: - Entry points (main, init, Test*, Benchmark*) - Exported symbols in main package - External/placeholder nodes - Interface methods (implementations satisfy interfaces)
Outputs:
[]DeadCodeNode - Potentially unused symbols with reasons.
Thread Safety: Safe for concurrent use.
func (*GraphAnalytics) DeadCodeWithCRS ¶
func (a *GraphAnalytics) DeadCodeWithCRS(ctx context.Context) ([]DeadCodeNode, crs.TraceStep)
DeadCodeWithCRS returns dead code analysis with a TraceStep.
Thread Safety: Safe for concurrent use.
func (*GraphAnalytics) DecomposePathWithCRS ¶
func (ga *GraphAnalytics) DecomposePathWithCRS(ctx context.Context, u, v string) ([]PathSegment, error)
DecomposePathWithCRS decomposes path and records a StepRecord to CRS.
Description:
Wraps HLD.DecomposePath() to provide CRS integration. Records the operation as a StepRecord with timing, outcome, and result summary.
Inputs:
- ctx: Context for cancellation and tracing. Must not be nil.
- u, v: Node IDs. Must not be empty.
Outputs:
- []PathSegment: Path segments (O(log V) segments).
- error: Non-nil on failure.
Thread Safety: Safe for concurrent use.
func (*GraphAnalytics) DetectCommunities ¶
func (a *GraphAnalytics) DetectCommunities(ctx context.Context, opts *LeidenOptions) (*CommunityResult, error)
DetectCommunities uses the Leiden algorithm to find natural code communities.
Description:
Implements the Leiden algorithm for community detection, which is an improvement over Louvain. The key difference is the refinement phase that guarantees all communities are well-connected. Algorithm phases: 1. Local moves: Try moving each node to neighbor's community if it improves modularity 2. Refinement: Ensure each community is well-connected (Leiden's key improvement) 3. Aggregation: Collapse communities into super-nodes (optional, for hierarchical) For code graphs, we implement phases 1 and 2 which are sufficient for typical codebase sizes (<100K nodes).
Inputs:
- ctx: Context for cancellation. Must not be nil.
- opts: Configuration options. If nil, defaults are used.
Outputs:
- *CommunityResult: Detected communities with modularity score.
- error: Non-nil if cancelled or other error.
Example:
opts := graph.DefaultLeidenOptions()
result, err := analytics.DetectCommunities(ctx, opts)
if err != nil {
return err
}
fmt.Printf("Found %d communities with modularity %.3f\n",
len(result.Communities), result.Modularity)
Thread Safety: Safe for concurrent use (read-only on graph).
Complexity: O(V + E) per iteration, typically few iterations.
Limitations:
- Single-threaded implementation
- Memory usage: O(V) for community assignments
func (*GraphAnalytics) DetectCommunitiesParallel ¶
func (a *GraphAnalytics) DetectCommunitiesParallel(ctx context.Context, opts *LeidenOptions) (*CommunityResult, error)
DetectCommunitiesParallel uses parallel processing for large graphs.
Description:
Parallelized version of DetectCommunities that uses multiple goroutines for pre-computation, refinement, and modularity calculation. Automatically falls back to sequential for graphs smaller than 1000 nodes.
Parallelization strategy:
- Pre-computation of degrees/neighbors: embarrassingly parallel
- Refinement phase: each community processed independently
- Modularity calculation: parallel sum reduction
- Local moves: sequential (correctness requires synchronization)
Inputs:
- ctx: Context for cancellation. Must not be nil.
- opts: Configuration options. If nil, defaults are used.
Outputs:
- *CommunityResult: Detected communities with modularity score.
- error: Non-nil if cancelled or other error.
Performance:
Speedup depends on graph structure and CPU count. Typical speedups: - 10K nodes: 2-3x faster - 100K nodes: 3-5x faster
Thread Safety: Safe for concurrent use.
func (*GraphAnalytics) DetectCommunitiesWithCRS ¶
func (a *GraphAnalytics) DetectCommunitiesWithCRS(ctx context.Context, opts *LeidenOptions) (*CommunityResult, crs.TraceStep)
DetectCommunitiesWithCRS detects communities and returns a TraceStep for CRS recording.
Description:
Wraps DetectCommunities with CRS integration for recording the operation in the reasoning trace.
Thread Safety: Safe for concurrent use.
func (*GraphAnalytics) DetectEntryNodes ¶
func (a *GraphAnalytics) DetectEntryNodes(ctx context.Context) []string
DetectEntryNodes finds nodes with no incoming edges.
Description:
Identifies natural entry points in the call graph - functions that are not called by any other function. These are typically main, init, test functions, or other top-level entry points.
Outputs:
[]string - Node IDs of entry points, prioritizing main/init names.
Thread Safety: Safe for concurrent use (read-only on graph).
func (*GraphAnalytics) DetectLoops ¶
func (a *GraphAnalytics) DetectLoops( ctx context.Context, domTree *DominatorTree, ) (*LoopNest, error)
DetectLoops identifies natural loops in the call graph.
Description:
Uses the dominator tree to find back edges (edges A→B where B dominates A), then constructs natural loops and their nesting hierarchy. Each back edge defines a natural loop with the target as the header. The algorithm: 1. Find all back edges using the dominator tree 2. For each header, compute the loop body via reverse BFS 3. Build the loop nesting hierarchy 4. Assign depths and parent/child relationships
Inputs:
- ctx: Context for cancellation. Checked between loop body computations.
- domTree: Pre-computed dominator tree. Must not be nil.
Outputs:
- *LoopNest: Complete loop nesting structure. Never nil.
- error: Non-nil if domTree is nil or context cancelled.
Example:
domTree, _ := analytics.Dominators(ctx, "main")
loops, err := analytics.DetectLoops(ctx, domTree)
if err != nil {
log.Fatal(err)
}
for _, loop := range loops.Loops {
log.Printf("Loop with header %s has %d nodes", loop.Header, loop.Size)
}
Limitations:
- Only detects natural loops (single entry point)
- Irreducible loops are not fully characterized (logged as warning)
- Requires dominator tree to be precomputed
Assumptions:
- domTree is valid and consistent
- Graph is frozen
Thread Safety: Safe for concurrent use (read-only on graph and domTree).
Complexity: O(V + E) after dominator tree.
func (*GraphAnalytics) DetectLoopsWithCRS ¶
func (a *GraphAnalytics) DetectLoopsWithCRS( ctx context.Context, domTree *DominatorTree, ) (*LoopNest, crs.TraceStep)
DetectLoopsWithCRS wraps DetectLoops with CRS tracing.
Description:
Provides the same functionality as DetectLoops but also returns a TraceStep for recording in the Code Reasoning State (CRS).
Thread Safety: Safe for concurrent use.
func (*GraphAnalytics) DetectSESERegions ¶
func (a *GraphAnalytics) DetectSESERegions( ctx context.Context, domTree, postDomTree *DominatorTree, ) (*SESEResult, error)
DetectSESERegions finds all SESE regions in the graph.
Description:
Uses dominator and post-dominator trees to identify regions with single entry and single exit points. A node N forms a SESE entry with its immediate post-dominator P if N dominates P. The algorithm iterates through all nodes and checks if each node can serve as a SESE entry with its immediate post-dominator as exit.
Inputs:
- ctx: Context for cancellation. Checked every 100 nodes.
- domTree: Pre-computed dominator tree. Must not be nil.
- postDomTree: Pre-computed post-dominator tree. Must not be nil.
Outputs:
- *SESEResult: All detected SESE regions with hierarchy. Never nil.
- error: Non-nil if inputs are nil or context cancelled.
Example:
domTree, _ := analytics.Dominators(ctx, "main")
postDomTree, _ := analytics.PostDominators(ctx, "")
result, err := analytics.DetectSESERegions(ctx, domTree, postDomTree)
if err != nil {
log.Fatalf("failed to detect SESE regions: %v", err)
}
for _, region := range result.Extractable {
log.Printf("extractable: %s -> %s (size: %d)", region.Entry, region.Exit, region.Size)
}
Limitations:
- Requires both dominator and post-dominator trees
- Only detects SESE regions for nodes in both trees
- Large graphs may produce many regions; use FilterExtractable()
Assumptions:
- domTree and postDomTree are computed from the same graph
- Graph is frozen
- Trees are consistent and valid
Thread Safety: Safe for concurrent use (read-only on trees).
Complexity: O(E) after dom trees are computed.
func (*GraphAnalytics) DetectSESERegionsWithCRS ¶
func (a *GraphAnalytics) DetectSESERegionsWithCRS( ctx context.Context, domTree, postDomTree *DominatorTree, ) (*SESEResult, crs.TraceStep)
DetectSESERegionsWithCRS wraps DetectSESERegions with CRS tracing.
Description:
Provides the same functionality as DetectSESERegions but also returns a TraceStep for recording in the Code Reasoning State (CRS).
Inputs:
- ctx: Context for cancellation.
- domTree: Pre-computed dominator tree.
- postDomTree: Pre-computed post-dominator tree.
Outputs:
- *SESEResult: All detected SESE regions.
- crs.TraceStep: Trace step for CRS recording.
Thread Safety: Safe for concurrent use.
func (*GraphAnalytics) DistanceWithCRS ¶
DistanceWithCRS computes distance and records a StepRecord to CRS.
Description:
Wraps HLD.Distance() to provide CRS integration. Records the operation as a StepRecord with timing, outcome, and result summary.
Inputs:
- ctx: Context for cancellation and tracing. Must not be nil.
- u, v: Node IDs. Must not be empty.
Outputs:
- int: Distance between nodes.
- error: Non-nil on failure.
Thread Safety: Safe for concurrent use.
func (*GraphAnalytics) Dominators ¶
func (a *GraphAnalytics) Dominators(ctx context.Context, entry string) (*DominatorTree, error)
Dominators computes the dominator tree using Cooper-Harvey-Kennedy algorithm.
Description:
Uses the iterative data-flow approach from "A Simple, Fast Dominance Algorithm" by Keith D. Cooper, Timothy J. Harvey, and Ken Kennedy (2001). This algorithm converges in O(E) time for typical reducible graphs and O(V²) worst case for non-reducible graphs.
Inputs:
- ctx: Context for cancellation. Must not be nil. Checked every iteration.
- entry: The entry node ID. Must exist in graph and not be empty.
Outputs:
- *DominatorTree: The computed dominator tree. Never nil.
- error: Non-nil if entry not found or context cancelled.
Example:
dt, err := analytics.Dominators(ctx, "main.go:10:main")
if err != nil {
log.Fatalf("failed to compute dominators: %v", err)
}
for node, idom := range dt.ImmediateDom {
log.Printf("%s is immediately dominated by %s", node, idom)
}
Limitations:
- Only computes dominators for nodes reachable from entry
- Unreachable nodes have no entry in ImmediateDom
- Memory usage: O(V) for all data structures
Assumptions:
- Graph is frozen (guaranteed by HierarchicalGraph construction)
- Entry node exists in the graph
- Directed edges represent call relationships
Thread Safety: Safe for concurrent use (read-only on graph).
Complexity: O(E) typical, O(V²) worst case.
func (*GraphAnalytics) DominatorsWithCRS ¶
func (a *GraphAnalytics) DominatorsWithCRS(ctx context.Context, entry string) (*DominatorTree, crs.TraceStep)
DominatorsWithCRS wraps Dominators with CRS tracing.
Description:
Provides the same functionality as Dominators but also returns a TraceStep for recording in the Code Reasoning State (CRS).
Thread Safety: Safe for concurrent use.
func (*GraphAnalytics) DominatorsWithCache ¶
func (a *GraphAnalytics) DominatorsWithCache(ctx context.Context, entry string) (*DominatorTree, crs.TraceStep, error)
DominatorsWithCache returns a cached dominator tree if available, computing a fresh tree only when needed.
Description:
Provides the same functionality as Dominators but caches the result to avoid recomputation when multiple tools request the dominator tree for the same entry point. The cache is automatically invalidated when: - A different entry point is requested - The underlying graph changes (detected via BuiltAtMilli)
Inputs:
- ctx: Context for cancellation. Must not be nil.
- entry: The entry node ID (must exist in graph).
Outputs:
- *DominatorTree: The computed or cached dominator tree. Never nil.
- crs.TraceStep: TraceStep for CRS recording (includes cache_hit metadata).
- error: Non-nil if entry not found or context cancelled.
Thread Safety: Safe for concurrent use via internal RWMutex.
func (*GraphAnalytics) EndSession ¶
func (ga *GraphAnalytics) EndSession(ctx context.Context) error
EndSession finalizes the current CRS session and clears history.
Description:
Clears the step history for the current session and resets session state. Call this when the session is complete to free memory.
Inputs:
- ctx: Context for cancellation. Must not be nil.
Outputs:
- error: Non-nil if CRS is not configured or no active session.
Thread Safety: Safe for concurrent use (protected by mutex).
func (*GraphAnalytics) GetCouplingForPackage ¶
func (a *GraphAnalytics) GetCouplingForPackage(pkg string) *CouplingMetrics
GetCouplingForPackage returns coupling metrics for a specific package.
Inputs:
pkg - The package path to analyze.
Outputs:
*CouplingMetrics - Metrics for the package, or nil if not found.
Thread Safety: Safe for concurrent use.
func (*GraphAnalytics) GetNode ¶
func (a *GraphAnalytics) GetNode(id string) (*Node, bool)
GetNode retrieves a node from the underlying graph by its ID.
Description:
Delegates to the underlying Graph.GetNode method to look up a node by its symbol ID. This is useful for checking node properties such as outgoing/incoming edge counts without exposing the internal graph.
Inputs:
id - The node ID (same as Symbol.ID).
Outputs:
*Node - The node if found, nil otherwise. bool - True if the node was found.
Thread Safety: Safe for concurrent use (graph is read-only after freeze).
func (*GraphAnalytics) HotSpots ¶
func (a *GraphAnalytics) HotSpots(top int) []HotspotNode
HotSpots returns the top N most-connected nodes in the graph.
Description:
Finds nodes with the highest connectivity score, where: score = inDegree * 2 + outDegree * 1 Incoming edges are weighted higher because being called frequently indicates higher impact on the codebase.
Inputs:
top - Maximum number of hotspots to return. Must be > 0.
Outputs:
[]HotspotNode - Top N nodes sorted by score descending.
Thread Safety: Safe for concurrent use.
func (*GraphAnalytics) HotSpotsWithCRS ¶
func (a *GraphAnalytics) HotSpotsWithCRS(ctx context.Context, top int) ([]HotspotNode, crs.TraceStep)
HotSpotsWithCRS returns hotspots with a TraceStep for CRS recording.
Thread Safety: Safe for concurrent use.
func (*GraphAnalytics) IsGraphReady ¶
func (a *GraphAnalytics) IsGraphReady() bool
IsGraphReady returns true if the graph has been fully indexed and is ready for queries.
Description:
Checks if the underlying graph has been populated with nodes and edges. This prevents race conditions where dominator-based tools execute before graph indexing completes, returning incorrect empty results.
Outputs:
- bool: true if graph is ready for queries, false if still indexing.
Thread Safety: Safe for concurrent use (read-only check).
Example:
if !analytics.IsGraphReady() {
return &Result{Success: false, Error: "graph not ready - indexing in progress"}, nil
}
Limitations:
This only checks if nodes/edges exist and graph is frozen. It doesn't validate that all files have been processed or that the graph is complete.
func (*GraphAnalytics) IsProductionFile ¶
func (a *GraphAnalytics) IsProductionFile(filePath string) bool
IsProductionFile returns true if the file is classified as production code.
Description:
Delegates to the underlying HierarchicalGraph.IsProductionFile(). This is the primary method that analytics tools should use to filter test/example/documentation files from results.
Inputs:
filePath - The file path to check.
Outputs:
bool - True if the file is production code. Returns true if analytics
or graph is nil (conservative default).
Thread Safety: Safe for concurrent use.
func (*GraphAnalytics) IsReducible ¶
func (a *GraphAnalytics) IsReducible(ctx context.Context, domTree *DominatorTree) (bool, error)
IsReducible provides a fast check for reducibility without region enumeration.
Description:
Returns true if the graph is reducible, false otherwise. This is faster than CheckReducibility when you only need the boolean result and don't need the detailed region information.
Complexity:
- Time: O(E × depth) worst case, but returns early on first cross edge
- Space: O(1)
Thread Safety: Safe for concurrent use.
func (*GraphAnalytics) LCAWithCRS ¶
LCAWithCRS computes LCA and records a StepRecord to CRS.
Description:
Wraps HLD.LCA() to provide CRS integration. Records the operation as a StepRecord with timing, outcome, and result summary. If CRS is not configured or no active session, falls through to raw HLD call.
Inputs:
- ctx: Context for cancellation and tracing. Must not be nil.
- u, v: Node IDs. Must not be empty.
Outputs:
- string: LCA node ID.
- error: Non-nil on failure.
Thread Safety: Safe for concurrent use.
func (*GraphAnalytics) LCDWithCRS ¶
func (a *GraphAnalytics) LCDWithCRS( ctx context.Context, domTree *DominatorTree, nodes []string, ) (string, crs.TraceStep)
LCDWithCRS wraps LCD query with CRS tracing.
Description:
Provides the same functionality as LowestCommonDominatorMultiple but also returns a TraceStep for recording in the Code Reasoning State (CRS).
Inputs:
- ctx: Context for cancellation. Must not be nil.
- domTree: Pre-computed dominator tree. Must not be nil.
- nodes: Slice of node IDs to find common dominator for.
Outputs:
- string: The LCD of all nodes.
- TraceStep: Trace step for CRS recording.
Thread Safety: Safe for concurrent use.
func (*GraphAnalytics) PackageCoupling ¶
func (a *GraphAnalytics) PackageCoupling() map[string]CouplingMetrics
PackageCoupling computes coupling metrics for all packages.
Description:
Computes Robert C. Martin's coupling metrics for each package: - Afferent Coupling (Ca): packages that depend ON this package - Efferent Coupling (Ce): packages this package depends ON - Instability (I): Ce / (Ca + Ce), range [0, 1] - I=0: maximally stable (many dependents, no dependencies) - I=1: maximally unstable (no dependents, all dependencies) - Abstractness (A): abstract types / total types
Outputs:
map[string]CouplingMetrics - Metrics keyed by package path.
Thread Safety: Safe for concurrent use.
func (*GraphAnalytics) PackageCouplingWithCRS ¶
func (a *GraphAnalytics) PackageCouplingWithCRS(ctx context.Context) (map[string]CouplingMetrics, crs.TraceStep)
PackageCouplingWithCRS returns coupling metrics with a TraceStep.
Thread Safety: Safe for concurrent use.
func (*GraphAnalytics) PageRank ¶
func (a *GraphAnalytics) PageRank(ctx context.Context, opts *PageRankOptions) *PageRankResult
PageRank computes PageRank scores for all nodes in the graph.
Description:
Uses power iteration to compute the PageRank score of each node, which represents its importance based on the importance of nodes linking to it (transitive importance). The algorithm handles sink nodes (nodes with no outgoing edges) by redistributing their PageRank evenly across all nodes, preventing rank "leakage" from the graph.
Inputs:
- ctx: Context for cancellation. Must not be nil.
- opts: Configuration options. If nil, defaults are used.
Outputs:
- *PageRankResult: Scores for all nodes, iteration count, convergence status. Returns empty result if graph is nil or empty.
Example:
opts := graph.DefaultPageRankOptions()
result := analytics.PageRank(ctx, opts)
if result.Converged {
fmt.Printf("Converged in %d iterations\n", result.Iterations)
}
Thread Safety: Safe for concurrent use.
Complexity: O(k × E) where k = iterations to converge (~20 typical).
Limitations:
- Single-threaded implementation
- Memory usage: 2 × V for score maps
func (*GraphAnalytics) PageRankTop ¶
func (a *GraphAnalytics) PageRankTop(ctx context.Context, k int, opts *PageRankOptions) []PageRankNode
PageRankTop returns the top-k nodes by PageRank score.
Description:
Computes PageRank for all nodes and returns the top-k ranked nodes. Each result includes the PageRank score and a comparison degree-based score for context.
Inputs:
- ctx: Context for cancellation. Must not be nil.
- k: Number of top nodes to return. Must be > 0.
- opts: Configuration options. If nil, defaults are used.
Outputs:
[]PageRankNode: Top-k nodes sorted by PageRank score descending.
Thread Safety: Safe for concurrent use.
func (*GraphAnalytics) PageRankTopWithCRS ¶
func (a *GraphAnalytics) PageRankTopWithCRS(ctx context.Context, k int, opts *PageRankOptions) ([]PageRankNode, crs.TraceStep)
PageRankTopWithCRS returns top-k nodes with a TraceStep for CRS recording.
Thread Safety: Safe for concurrent use.
func (*GraphAnalytics) PageRankWithCRS ¶
func (a *GraphAnalytics) PageRankWithCRS(ctx context.Context, opts *PageRankOptions) (*PageRankResult, crs.TraceStep)
PageRankWithCRS computes PageRank and returns a TraceStep for CRS recording.
Thread Safety: Safe for concurrent use.
func (*GraphAnalytics) PathGCDWithCRS ¶
func (ga *GraphAnalytics) PathGCDWithCRS(ctx context.Context, u, v string, valueFunc func(string) int64) (int64, error)
PathGCDWithCRS computes GCD of values on path from u to v with CRS recording.
Description:
Computes the GCD of node values along the path from u to v using HLD and segment tree. If BadgerDB caching is enabled, checks cache before computing.
Thread Safety: Safe for concurrent use.
func (*GraphAnalytics) PathMaxWithCRS ¶
func (ga *GraphAnalytics) PathMaxWithCRS(ctx context.Context, u, v string, valueFunc func(string) int64) (int64, error)
PathMaxWithCRS computes maximum value on path from u to v with CRS recording.
Description:
Computes the maximum of node values along the path from u to v using HLD and segment tree. If BadgerDB caching is enabled, checks cache before computing.
Thread Safety: Safe for concurrent use.
func (*GraphAnalytics) PathMinWithCRS ¶
func (ga *GraphAnalytics) PathMinWithCRS(ctx context.Context, u, v string, valueFunc func(string) int64) (int64, error)
PathMinWithCRS computes minimum value on path from u to v with CRS recording.
Description:
Computes the minimum of node values along the path from u to v using HLD and segment tree. If BadgerDB caching is enabled, checks cache before computing.
Thread Safety: Safe for concurrent use.
func (*GraphAnalytics) PathSumWithCRS ¶
func (ga *GraphAnalytics) PathSumWithCRS(ctx context.Context, u, v string, valueFunc func(string) int64) (int64, error)
PathSumWithCRS computes sum of values on path from u to v with CRS recording.
Description:
Computes the sum of node values along the path from u to v using HLD and segment tree. Records operation to CRS for traceability and replay. If BadgerDB caching is enabled, checks cache before computing.
Inputs:
- ctx: Context for cancellation. Must not be nil.
- u: Start node ID. Must exist in graph.
- v: End node ID. Must exist in graph.
- valueFunc: Function that returns value for each node. Must not be nil.
Outputs:
- int64: Sum of values on path u → v.
- error: Non-nil if nodes don't exist or query fails.
Thread Safety: Safe for concurrent use.
func (*GraphAnalytics) PopSession ¶
func (ga *GraphAnalytics) PopSession(ctx context.Context) error
PopSession ends the current nested session and restores the previous session from the stack.
Description:
Ends the current session, clears its history, and restores the previous session state from the stack. This is the counterpart to PushSession().
Inputs:
- ctx: Context for cancellation. Must not be nil.
Outputs:
- error: Non-nil if CRS is not configured, no active session, or stack is empty.
Thread Safety: Safe for concurrent use (protected by mutex).
func (*GraphAnalytics) PostDominators ¶
func (a *GraphAnalytics) PostDominators(ctx context.Context, exit string) (*DominatorTree, error)
PostDominators computes the post-dominator tree using Cooper-Harvey-Kennedy algorithm.
Description:
Computes post-dominators by running the dominator algorithm on the reversed graph. A node A post-dominates B if every path from B to exit must go through A. This is the dual of dominators: dominators answer "what must happen before X" while post-dominators answer "what must happen after X". If exit is empty, auto-detects exit nodes (nodes with no outgoing edges). If multiple exits are detected, creates a virtual exit node internally.
Inputs:
- ctx: Context for cancellation. Must not be nil. Checked every iteration.
- exit: The exit node ID. If empty, auto-detects exits.
Outputs:
- *DominatorTree: The computed post-dominator tree. Never nil. The Entry field contains the exit node (or virtual exit ID).
- error: Non-nil if exit not found or context cancelled.
Example:
dt, err := analytics.PostDominators(ctx, "cleanup")
if err != nil {
log.Fatalf("failed to compute post-dominators: %v", err)
}
// dt.ImmediateDom maps each node to its immediate post-dominator
for node, ipostdom := range dt.ImmediateDom {
log.Printf("%s is immediately post-dominated by %s", node, ipostdom)
}
Limitations:
- Only computes post-dominators for nodes that can reach exit
- Nodes that cannot reach exit have no entry in ImmediateDom
- Memory usage: O(V) for all data structures
Assumptions:
- Graph is frozen (guaranteed by HierarchicalGraph construction)
- Exit node exists in the graph (if specified)
- Directed edges represent call relationships
Thread Safety: Safe for concurrent use (read-only on graph).
Complexity: O(E) typical, O(V²) worst case.
func (*GraphAnalytics) PostDominatorsWithCRS ¶
func (a *GraphAnalytics) PostDominatorsWithCRS(ctx context.Context, exit string) (*DominatorTree, crs.TraceStep)
PostDominatorsWithCRS wraps PostDominators with CRS tracing.
Description:
Provides the same functionality as PostDominators but also returns a TraceStep for recording in the Code Reasoning State (CRS).
Thread Safety: Safe for concurrent use.
func (*GraphAnalytics) PostDominatorsWithCache ¶
func (a *GraphAnalytics) PostDominatorsWithCache(ctx context.Context, exit string) (*DominatorTree, crs.TraceStep, error)
PostDominatorsWithCache returns a cached post-dominator tree if available, computing a fresh tree only when needed.
Description:
Provides the same functionality as PostDominators but caches the result to avoid recomputation when multiple tools request the post-dominator tree for the same exit point. The cache is automatically invalidated when: - A different exit point is requested - The underlying graph changes (detected via BuiltAtMilli)
Inputs:
- ctx: Context for cancellation. Must not be nil.
- exit: The exit node ID. If empty, auto-detects exits.
Outputs:
- *DominatorTree: The computed or cached post-dominator tree. Never nil.
- crs.TraceStep: TraceStep for CRS recording (includes cache_hit metadata).
- error: Non-nil if exit not found or context cancelled.
Thread Safety: Safe for concurrent use via internal RWMutex.
func (*GraphAnalytics) PushSession ¶
func (ga *GraphAnalytics) PushSession(ctx context.Context, sessionID string) error
PushSession suspends the current session and starts a new nested session.
Description:
Saves the current session state onto a stack and starts a new session with the given sessionID. This allows nested/hierarchical session tracking. Use PopSession() to end the nested session and restore the previous one.
Inputs:
- ctx: Context for cancellation. Must not be nil.
- sessionID: Unique session identifier for the nested session. Must not be empty.
Outputs:
- error: Non-nil if CRS is not configured, session ID is empty, or no active session to push.
Thread Safety: Safe for concurrent use (protected by mutex).
Example:
// Outer operation analytics.StartSession(ctx, "outer-session") _, _ = analytics.LCAWithCRS(ctx, "node1", "node2") // Start nested operation analytics.PushSession(ctx, "inner-session") _, _ = analytics.LCAWithCRS(ctx, "node3", "node4") analytics.PopSession(ctx) // Back to outer operation _, _ = analytics.LCAWithCRS(ctx, "node5", "node6") analytics.EndSession(ctx)
func (*GraphAnalytics) ShortestPath ¶
func (a *GraphAnalytics) ShortestPath(ctx context.Context, fromID, toID string) (*PathResult, error)
ShortestPath finds the shortest path between two symbols in the graph.
Description:
D3c: Accessor that delegates to the underlying Graph's ShortestPath method. Used by filterByReachability to check if a candidate is reachable from the from-side symbol.
Inputs:
- ctx: Context for cancellation.
- fromID: Source symbol ID.
- toID: Destination symbol ID.
Outputs:
- *PathResult: Path details or nil if no path exists.
- error: Non-nil if graph is unavailable or nodes not found.
Thread Safety: Safe for concurrent use.
func (*GraphAnalytics) StartSession ¶
func (ga *GraphAnalytics) StartSession(ctx context.Context, sessionID string) error
StartSession initializes a new CRS session for query recording.
Description:
Sets the session ID in CRS and resets the step counter. All subsequent CRS-aware queries (LCAWithCRS, etc.) will record StepRecords to this session.
Inputs:
- ctx: Context for cancellation. Must not be nil.
- sessionID: Unique session identifier. Must not be empty.
Outputs:
- error: Non-nil if CRS is not configured or session ID is empty.
Thread Safety: Safe for concurrent use (protected by mutex).
type GraphAnalyticsOptions ¶
type GraphAnalyticsOptions struct {
// AutoSession enables automatic session management.
// When true, CRS-aware queries automatically start a session on first use.
// User must still call EndSession() to finalize and free resources.
// Default: false (manual session management required)
AutoSession bool
// SessionTimeout defines the maximum inactivity duration before a session expires.
// When set, if a CRS-aware query is called and the session has been inactive
// for longer than SessionTimeout, the old session is automatically ended and
// a new session is started.
// A value of 0 disables timeout (sessions never expire from inactivity).
// Default: 0 (no timeout)
SessionTimeout time.Duration
// EnableBadgerQueryCache enables cross-session path query caching via BadgerDB.
// When true, path query results are persisted to BadgerDB and reused across sessions
// if the graph hash matches (graph unchanged).
// Cache invalidation: automatic on graph hash change.
// Default: false (no persistent caching)
EnableBadgerQueryCache bool
// BadgerDB is the BadgerDB instance for persistent caching.
// Required if EnableBadgerQueryCache is true, otherwise ignored.
// Must be opened and managed by caller.
BadgerDB *badger.DB
}
GraphAnalyticsOptions configures GraphAnalytics behavior.
type GraphHolder ¶
type GraphHolder struct {
// contains filtered or unexported fields
}
GraphHolder provides thread-safe access to a mutable graph reference.
Description:
Wraps a graph pointer with a mutex for thread-safe swapping. Used by Refresher to atomically update the graph.
Thread Safety:
All methods are safe for concurrent use.
func NewGraphHolder ¶
func NewGraphHolder(g *Graph) *GraphHolder
NewGraphHolder creates a new GraphHolder.
func (*GraphHolder) GetPtr ¶
func (h *GraphHolder) GetPtr() **Graph
GetPtr returns a pointer to the graph pointer.
Description:
Returns a pointer suitable for use with NewRefresher. The returned pointer should only be used with the Refresher.
type GraphOption ¶
type GraphOption func(*GraphOptions)
GraphOption is a functional option for configuring Graph.
func WithMaxEdges ¶
func WithMaxEdges(n int) GraphOption
WithMaxEdges sets the maximum number of edges the graph can hold.
func WithMaxNodes ¶
func WithMaxNodes(n int) GraphOption
WithMaxNodes sets the maximum number of nodes the graph can hold.
type GraphOptions ¶
type GraphOptions struct {
// MaxNodes is the maximum number of nodes the graph can hold.
// Default: 1,000,000
MaxNodes int
// MaxEdges is the maximum number of edges the graph can hold.
// Default: 10,000,000
MaxEdges int
}
GraphOptions configures Graph behavior and limits.
func DefaultGraphOptions ¶
func DefaultGraphOptions() GraphOptions
DefaultGraphOptions returns sensible defaults for graph configuration.
type GraphState ¶
type GraphState int
GraphState represents the lifecycle state of the graph.
const ( // GraphStateBuilding indicates the graph is accepting AddNode/AddEdge calls. GraphStateBuilding GraphState = iota // GraphStateReadOnly indicates the graph is frozen and read-only. GraphStateReadOnly )
func (GraphState) String ¶
func (s GraphState) String() string
String returns the string representation of the GraphState.
type GraphStats ¶
type GraphStats struct {
// NodeCount is the total number of nodes.
NodeCount int
// EdgeCount is the total number of edges.
EdgeCount int
// EdgesByType maps each EdgeType to the count of edges of that type.
EdgesByType map[EdgeType]int
// NodesByKind maps each SymbolKind to the count of nodes of that kind.
// Added for GR-43 debug endpoint.
NodesByKind map[ast.SymbolKind]int
// MaxNodes is the configured maximum node capacity.
MaxNodes int
// MaxEdges is the configured maximum edge capacity.
MaxEdges int
// State is the current graph state.
State GraphState
// BuiltAtMilli is when Freeze() was called (0 if not frozen).
BuiltAtMilli int64
}
GraphStats contains statistics about the graph.
Thread Safety: GraphStats is a value type with no internal state. Safe for concurrent use as long as the source Graph is frozen.
type HLDCache ¶
type HLDCache interface {
// GetLCA returns cached LCA result, or (empty, false) if not found.
GetLCA(u, v string) (lca string, found bool)
// SetLCA stores LCA result with optional TTL.
SetLCA(u, v, lca string, ttl time.Duration)
// GetDistance returns cached distance, or (0, false) if not found.
GetDistance(u, v string) (distance int, found bool)
// SetDistance stores distance result with optional TTL.
SetDistance(u, v string, distance int, ttl time.Duration)
// GetPath returns cached path segments, or (nil, false) if not found.
GetPath(u, v string) (segments []PathSegment, found bool)
// SetPath stores path segments with optional TTL.
SetPath(u, v string, segments []PathSegment, ttl time.Duration)
// Invalidate removes all cached entries (called when graph changes).
Invalidate()
// Stats returns cache statistics.
Stats() CacheStats
}
HLDCache provides caching for HLD query results.
Description:
Optional interface for caching expensive query results across invocations. Implementations can use in-memory caches (LRU, TTL) or persistent stores (BadgerDB, Redis) depending on requirements.
Thread Safety: All methods must be safe for concurrent use.
type HLDForest ¶
type HLDForest struct {
// contains filtered or unexported fields
}
HLDForest represents multiple HLD decompositions for a forest (disconnected graph).
Description:
A forest is a collection of disconnected trees. This structure maintains separate HLD decompositions for each tree in the forest.
Thread Safety:
Read-only after construction. Safe for concurrent queries.
func BuildHLDForest ¶
BuildHLDForest constructs HLD for each tree in a forest.
Description:
Detects all connected components in the graph and builds separate HLD decompositions for each tree. Useful for graphs that may have multiple disconnected subgraphs.
Algorithm:
Time: O(V + E) where V = nodes, E = edges Space: O(V) for each tree
Inputs:
- ctx: Context for cancellation. Must not be nil.
- g: Graph (may be forest). Must be frozen, acyclic.
- useIterative: If true, uses iterative DFS (recommended for deep trees).
Outputs:
- *HLDForest: Forest structure with HLD for each tree. Never nil on success.
- error: Non-nil if graph contains cycles or operation is cancelled.
Example:
forest, err := BuildHLDForest(ctx, graph, true)
if err != nil {
return fmt.Errorf("build forest: %w", err)
}
fmt.Printf("Forest has %d trees\n", forest.TreeCount())
Thread Safety: Safe for concurrent use with different graphs.
func (*HLDForest) GetHLD ¶
func (f *HLDForest) GetHLD(nodeID string) *HLDecomposition
GetHLD returns the HLD decomposition for the tree containing the given node.
Returns nil if node is not found in any tree.
func (*HLDForest) GetHLDByIndex ¶
func (f *HLDForest) GetHLDByIndex(index int) *HLDecomposition
GetHLDByIndex returns the HLD decomposition for the tree at the given index.
Returns nil if index is out of bounds.
func (*HLDForest) GetRoot ¶
GetRoot returns the root node ID for the tree at the given index.
Returns empty string if index is out of bounds.
func (*HLDForest) GetTreeID ¶
GetTreeID returns the tree index for a given node.
Description:
Returns the index of the tree containing the specified node. Used for validating that two nodes are in the same tree component.
Inputs:
- nodeID: Node to look up. Must exist in forest.
Outputs:
- int: Tree index (0-based).
- error: Non-nil if node not found.
Thread Safety: Safe for concurrent use (read-only).
func (*HLDForest) GetTreeOffset ¶
GetTreeOffset returns the segment tree position offset for a node's tree.
Description:
Calculates the cumulative position offset for the tree containing nodeID. In a forest, segment tree positions are laid out sequentially: - Tree 0: positions [0, tree0.NodeCount()) - Tree 1: positions [tree0.NodeCount(), tree0.NodeCount() + tree1.NodeCount()) - etc. To get global segment tree position from tree-local HLD position: globalPos = GetTreeOffset(nodeID) + treeHLD.Pos(nodeIdx)
Algorithm:
Time: O(T) where T = number of trees (typically small) Space: O(1)
Inputs:
- nodeID: Node to get tree offset for. Must exist in forest.
Outputs:
- int: Position offset for this node's tree (0 for tree 0).
- error: Non-nil if node not found in forest.
Example:
// Forest with 2 trees: tree0 has 3 nodes, tree1 has 4 nodes
// Segment tree positions: [0,1,2 | 3,4,5,6]
offset, _ := forest.GetTreeOffset("nodeInTree1") // Returns 3
treeHLD := forest.GetHLD("nodeInTree1")
nodeIdx, _ := treeHLD.NodeToIdx("nodeInTree1")
treePos := treeHLD.Pos(nodeIdx) // e.g., 0 (local position)
globalPos := offset + treePos // 3 + 0 = 3 (global position)
Thread Safety: Safe for concurrent use (read-only).
func (*HLDForest) GraphHash ¶
GraphHash returns combined hash of all trees in the forest.
Description:
Combines graph hashes from all HLD decompositions to create a unique identifier for the forest structure. Used for cache invalidation when graph changes.
Thread Safety: Safe for concurrent use (read-only).
func (*HLDForest) TotalNodes ¶
TotalNodes returns the total number of nodes across all trees in the forest.
Description:
Sums NodeCount() across all HLD decompositions. Used for validating segment tree size compatibility.
Thread Safety: Safe for concurrent use (read-only).
type HLDStats ¶
type HLDStats struct {
NodeCount int
HeavyPathCount int
AvgPathLength float64
MaxPathLength int
LightEdgeCount int
ConstructionTime time.Duration
}
HLDStats contains statistics about the HLD structure.
type HLDecomposition ¶
type HLDecomposition struct {
// contains filtered or unexported fields
}
HLDecomposition represents a Heavy-Light Decomposition of a tree.
Description:
Decomposes a tree into heavy and light paths, enabling O(log V) path queries and O(log² V) aggregate queries when combined with segment trees. The decomposition is computed via DFS and remains static until rebuilt.
Invariants:
- All arrays have length V (number of nodes)
- parent[root] == -1
- heavy[v] == -1 iff v is leaf or all children have subtree size 1
- head[v] == v iff v starts a new heavy path
- Positions form valid DFS ordering: parent before children
- Subtree v occupies positions [pos[v], pos[v]+subSize[v])
Thread Safety:
Read-only after construction. Safe for concurrent queries.
func BuildHLD ¶
BuildHLD constructs a Heavy-Light Decomposition from a tree graph.
Description:
Performs three-pass DFS to compute HLD structure: 1. First DFS: Compute subtree sizes, depths, parents 2. Heavy child selection: Identify heavy child for each node 3. Second DFS: Assign heads and positions in DFS order
Algorithm:
Time: O(V) where V = number of nodes Space: O(V) for arrays, O(log V) for recursion stack
Inputs:
- ctx: Context for cancellation. Must not be nil.
- g: Tree graph. Must be frozen, acyclic, connected.
- root: Root node ID. Must exist in graph.
Outputs:
- *HLDecomposition: Constructed HLD structure. Never nil on success.
- error: Non-nil if graph is invalid (cyclic, disconnected, not frozen).
Optimizations:
- Single allocation for all arrays (reduces GC pressure)
- Iterative DFS where possible (reduces stack depth)
- Pre-computed node count (avoids multiple traversals)
- Inline heavy child selection (avoids separate pass)
CRS Integration:
- Records construction as TraceStep with timing and metadata
- Includes node count, root, light edge count
- Enables caching of HLD structure across sessions
Example:
hld, err := BuildHLD(ctx, callGraph, "main")
if err != nil {
return fmt.Errorf("build HLD: %w", err)
}
// hld is ready for queries
Thread Safety: Safe for concurrent use with different graphs.
func BuildHLDIterative ¶
BuildHLDIterative constructs HLD using iterative DFS (no recursion).
Description:
Identical to BuildHLD but uses iterative DFS to avoid stack overflow for deep trees (>10K depth). Recommended for production use with large or unknown graph structures.
Algorithm:
Time: O(V) where V = number of nodes Space: O(V) for arrays + O(V) for explicit DFS stack
Inputs:
- ctx: Context for cancellation. Must not be nil.
- g: Tree graph. Must be frozen, acyclic, connected.
- root: Root node ID. Must exist in graph.
Outputs:
- *HLDecomposition: Constructed HLD structure. Never nil on success.
- error: Non-nil if graph is invalid or operation is cancelled.
Differences from BuildHLD:
- Uses iterative DFS (explicit stack) instead of recursive DFS
- No stack overflow risk for deep trees
- Slightly more memory overhead (explicit stack)
Example:
// For large or deep graphs
hld, err := BuildHLDIterative(ctx, callGraph, "main")
if err != nil {
return fmt.Errorf("build HLD: %w", err)
}
Thread Safety: Safe for concurrent use with different graphs.
func (*HLDecomposition) BatchDecomposePath ¶
func (hld *HLDecomposition) BatchDecomposePath(ctx context.Context, pairs [][2]string) ([][]PathSegment, []error, error)
BatchDecomposePath decomposes multiple paths in parallel.
Description:
Similar to BatchLCA but returns path segments.
Thread Safety: Safe for concurrent use.
func (*HLDecomposition) BatchDistance ¶
func (hld *HLDecomposition) BatchDistance(ctx context.Context, pairs [][2]string) ([]int, []error, error)
BatchDistance computes distance for multiple node pairs in parallel.
Description:
Similar to BatchLCA but returns distances instead of LCA nodes.
Thread Safety: Safe for concurrent use.
func (*HLDecomposition) BatchLCA ¶
func (hld *HLDecomposition) BatchLCA(ctx context.Context, pairs [][2]string) ([]string, []error, error)
BatchLCA computes LCA for multiple node pairs in parallel.
Description:
Executes multiple LCA queries concurrently, bounded by available CPUs. Useful for bulk analysis and report generation. Each query is independent and uses the same HLD structure.
Algorithm:
Time: O(N * log V) where N = number of pairs Space: O(N) for results Parallelism: Limited to runtime.NumCPU() concurrent queries
Inputs:
- ctx: Context for cancellation. Cancels all pending queries.
- pairs: Slice of [2]string, each containing (u, v) node IDs.
Outputs:
- results: Slice of LCA results, same length as pairs. Empty string on error.
- errors: Slice of errors, same length as pairs. Nil on success.
- error: Non-nil only if validation fails. Individual query errors in errors slice.
Performance:
- 5-10x faster than sequential queries for N > 100
- Limited by CPU count, not I/O
- Memory usage: O(N) for results
Example:
pairs := [][2]string{{"A", "B"}, {"C", "D"}, {"E", "F"}}
results, errs, err := hld.BatchLCA(ctx, pairs)
if err != nil {
return fmt.Errorf("batch LCA: %w", err)
}
for i, result := range results {
if errs[i] != nil {
log.Warn("Query failed", "pair", pairs[i], "error", errs[i])
continue
}
fmt.Printf("LCA(%s, %s) = %s\n", pairs[i][0], pairs[i][1], result)
}
Thread Safety: Safe for concurrent use.
func (*HLDecomposition) CacheKey ¶
func (hld *HLDecomposition) CacheKey() string
CacheKey generates a cache key for this HLD structure.
Description:
Returns a deterministic cache key based on root node ID. For full graph hashing, use with external graph hash.
Thread Safety: Safe for concurrent use (read-only).
func (*HLDecomposition) CacheKeyWithGraphHash ¶
func (hld *HLDecomposition) CacheKeyWithGraphHash(graphHash string) string
CacheKeyWithGraphHash generates a cache key using pre-computed graph hash.
Description:
More efficient than CacheKey() when graph hash is already available. Use this when graph provides its own hash.
Thread Safety: Safe for concurrent use (read-only).
func (*HLDecomposition) DecomposePath ¶
func (hld *HLDecomposition) DecomposePath(ctx context.Context, u, v string) (segments []PathSegment, err error)
DecomposePath decomposes a path into O(log V) heavy path segments.
Description:
Breaks the path from u to v into contiguous segments, each lying entirely on a single heavy path. Returns segments as [Start, End] position ranges suitable for segment tree queries.
Algorithm:
Compute lca = LCA(u, v)
Decompose upward path u → lca into segments
Decompose upward path v → lca into segments
Return combined list of segments
Time: O(log V) - at most O(log V) segments Space: O(log V) - segment slice
Inputs:
- ctx: Context for cancellation. Must not be nil.
- u: Start node ID. Must exist in graph.
- v: End node ID. Must exist in graph.
Outputs:
- []PathSegment: List of segments covering path u → v. Nil on error.
- error: Non-nil if nodes don't exist or are in different trees.
Ordering:
Segments are ordered from leaf to root for each branch: - First: segments from u upward to lca - Then: segments from v upward to lca This ensures correct aggregation order for non-commutative operations.
Memory Management (P-C1 fix):
P-C1: Removed sync.Pool as it added overhead without benefit. Now uses simple allocation - caller owns the returned slice. For batch workloads, see BatchDecomposePath (future enhancement).
Example:
segments, err := hld.DecomposePath(ctx, "funcA", "funcB")
if err != nil {
return fmt.Errorf("decompose path: %w", err)
}
for _, seg := range segments {
value := segmentTree.Query(seg.Start, seg.End)
// Aggregate values...
}
Limitations:
- Only works on trees
- Nodes must be in same tree component
- Allocates O(log V) memory for segments
Assumptions:
- HLD structure is valid
- Segment positions are within bounds
Thread Safety: Safe for concurrent use (allocates new slice per call).
func (*HLDecomposition) Depth ¶
func (hld *HLDecomposition) Depth(v int) int
Depth returns the depth of node v from root.
func (*HLDecomposition) Distance ¶
Distance computes the number of edges on the path between u and v.
Description:
Uses LCA to compute distance: dist(u, v) = depth[u] + depth[v] - 2*depth[lca] Formula is derived from tree property: any path goes through LCA.
Algorithm:
Time: O(log V) - dominated by LCA query Space: O(1) - no allocations
Inputs:
- ctx: Context for cancellation. Must not be nil.
- u: Start node ID. Must exist in graph.
- v: End node ID. Must exist in graph.
Outputs:
- int: Number of edges on path from u to v.
- error: Non-nil if nodes don't exist or are in different trees.
Special Cases:
- Distance(u, u) = 0
- Distance(u, parent[u]) = 1
Performance Note (P-M3):
If you need both LCA and distance, call LCA() first and compute distance directly from depths to avoid redundant LCA computation: lca, _ := hld.LCA(ctx, u, v) uIdx, vIdx := hld.nodeToIdx[u], hld.nodeToIdx[v] lcaIdx := hld.nodeToIdx[lca] dist := hld.depth[uIdx] + hld.depth[vIdx] - 2*hld.depth[lcaIdx]
Example:
dist, err := hld.Distance(ctx, "main", "helper")
if err != nil {
return fmt.Errorf("compute distance: %w", err)
}
fmt.Printf("Distance: %d edges\n", dist)
Limitations:
- Only works on trees
- Nodes must be in same tree component
Assumptions:
- HLD structure is valid
- Depth array is correct
Thread Safety: Safe for concurrent use (read-only).
func (*HLDecomposition) EstimateDistanceCost ¶
func (hld *HLDecomposition) EstimateDistanceCost(u, v string) (int, error)
EstimateDistanceCost estimates the cost of a distance query. Same as LCA cost since distance computation is dominated by LCA.
func (*HLDecomposition) EstimateLCACost ¶
func (hld *HLDecomposition) EstimateLCACost(u, v string) (int, error)
EstimateLCACost estimates the computational cost of an LCA query.
Description:
Provides conservative estimate of query cost based on node depths. Cost represents approximate number of iterations needed. Can be used for query prioritization or rejection.
Algorithm:
cost = max(depth[u], depth[v]) Worst case: O(depth) iterations when nodes are on different branches.
Inputs:
- u: First node ID. Must exist in graph.
- v: Second node ID. Must exist in graph.
Outputs:
- int: Estimated cost (higher = more expensive query)
- error: Non-nil if nodes don't exist
Use Cases:
- Reject queries exceeding cost budget
- Prioritize cheap queries in batch processing
- Monitor query patterns for optimization
Example:
cost, err := hld.EstimateLCACost("deepNode", "anotherDeepNode")
if err != nil {
return fmt.Errorf("estimate cost: %w", err)
}
if cost > maxAllowedCost {
return errors.New("query too expensive")
}
Thread Safety: Safe for concurrent use (read-only).
func (*HLDecomposition) EstimatePathCost ¶
func (hld *HLDecomposition) EstimatePathCost(u, v string) (int, error)
EstimatePathCost estimates the cost of a path decomposition query. Returns 2x LCA cost (decompose both u→lca and v→lca).
func (*HLDecomposition) GetLCAStats ¶
func (hld *HLDecomposition) GetLCAStats() LCAStats
GetLCAStats returns statistics about LCA queries.
Description:
Returns aggregated statistics tracked atomically during LCA queries. Provides insights into query performance and iteration counts.
Note on Consistency (A-H2):
Stats are eventually consistent. The four atomic fields are loaded separately, so in high-concurrency scenarios they may represent slightly different points in time. For most monitoring use cases, this eventual consistency is acceptable.
Outputs:
- LCAStats: Statistics about LCA queries
Example (CM-C1):
stats := hld.GetLCAStats()
fmt.Printf("Queries: %d, Avg iterations: %.1f, Max: %d\n",
stats.QueryCount, stats.AvgIterations, stats.MaxIterations)
fmt.Printf("Total time: %d ms\n", stats.TotalDurationMs)
Thread Safety: Safe for concurrent use (atomic loads).
func (*HLDecomposition) GraphHash ¶
func (hld *HLDecomposition) GraphHash() string
GraphHash returns the hash of the graph when HLD was built.
func (*HLDecomposition) Head ¶
func (hld *HLDecomposition) Head(v int) int
Head returns the head of the heavy path containing node v.
func (*HLDecomposition) HeavyChild ¶
func (hld *HLDecomposition) HeavyChild(v int) int
HeavyChild returns the heavy child of node v (-1 if none).
func (*HLDecomposition) IdxToNode ¶
func (hld *HLDecomposition) IdxToNode(idx int) (string, error)
IdxToNode returns the node ID for an internal index.
func (*HLDecomposition) InitStats ¶
func (hld *HLDecomposition) InitStats()
InitStats initializes LCA query statistics to zero.
Description:
Called automatically during HLD construction to ensure clean state. Can also be called to reset stats without rebuilding HLD.
Thread Safety: NOT safe for concurrent use with query operations.
Call only during construction or when queries are paused.
func (*HLDecomposition) IsAncestor ¶
IsAncestor checks if u is an ancestor of v.
Description:
Node u is an ancestor of v iff LCA(u, v) == u. Uses LCA query internally, so has same O(log V) complexity.
Algorithm:
Time: O(log V) - requires LCA query Space: O(1)
Inputs:
- ctx: Context for cancellation. Must not be nil.
- u: Potential ancestor node ID. Must exist in graph.
- v: Potential descendant node ID. Must exist in graph.
Outputs:
- bool: True if u is ancestor of v (or u == v).
- error: Non-nil if nodes don't exist or are in different trees.
Example (CM-C1):
isAncestor, err := hld.IsAncestor(ctx, "main", "helper")
if err != nil {
return fmt.Errorf("check ancestry: %w", err)
}
if isAncestor {
fmt.Println("main calls helper (directly or indirectly)")
}
Assumptions (CM-H1):
- HLD has been built successfully
- Graph structure hasn't changed since construction
Thread Safety: Safe for concurrent use (read-only).
func (*HLDecomposition) LCA ¶
LCA computes the Lowest Common Ancestor of two nodes.
Description:
Finds the deepest node that is an ancestor of both u and v using Heavy-Light Decomposition. Repeatedly jumps up light edges until both nodes are on the same heavy path, then returns the shallower one.
Algorithm:
Time: O(log V) - at most O(log V) light edge jumps Space: O(1) - no allocations
Inputs:
- ctx: Context for cancellation. Must not be nil.
- u: First node ID. Must exist in graph.
- v: Second node ID. Must exist in graph.
Outputs:
- lca: Node ID of LCA. Empty string on error.
- err: Non-nil if nodes don't exist, are in different trees, or HLD invalid.
Special Cases:
- LCA(u, u) = u
- LCA(u, parent[u]) = parent[u]
- LCA(u, v) where u is ancestor of v = u
Example:
lca, err := hld.LCA(ctx, "funcA", "funcB")
if err != nil {
return fmt.Errorf("compute LCA: %w", err)
}
fmt.Printf("LCA of funcA and funcB: %s\n", lca)
Limitations:
- SINGLE-TREE ONLY: This implementation does not support forest mode. For graphs with multiple disconnected tree components, nodes must be in the same component. Cross-tree queries will return incorrect results. Use HLDForest for multi-tree support when implemented.
- Graph must not change after HLD construction
Assumptions:
- HLD has been built successfully via BuildHLD or BuildHLDIterative
- Graph structure hasn't changed since HLD construction
- For forest graphs, caller has validated nodes are in same tree
Thread Safety:
Safe for concurrent use. All methods use pointer receivers because: 1. HLD struct is large (~10+ arrays) 2. Methods update atomic stats fields 3. Read-only operations are safe for concurrent use
func (*HLDecomposition) NodeAtPos ¶
func (hld *HLDecomposition) NodeAtPos(i int) int
NodeAtPos returns the node at DFS position i.
func (*HLDecomposition) NodeCount ¶
func (hld *HLDecomposition) NodeCount() int
NodeCount returns the number of nodes in the HLD.
func (*HLDecomposition) NodeToIdx ¶
func (hld *HLDecomposition) NodeToIdx(nodeID string) (int, bool)
NodeToIdx returns the internal index for a node ID.
func (*HLDecomposition) Parent ¶
func (hld *HLDecomposition) Parent(v int) int
Parent returns the parent index of node v (-1 for root).
func (*HLDecomposition) PathNodes ¶
PathNodes returns all node IDs on the path from u to v.
Description:
Materializes the actual path as a list of node IDs from u to v. Useful for debugging and visualization, but slower than decomposition.
Algorithm:
Compute lca = LCA(u, v)
Collect nodes from u upward to lca
Collect nodes from v upward to lca (in reverse order)
Concatenate both paths with lca in middle
Time: O(distance(u,v)) - proportional to path length Space: O(distance(u,v)) - stores all node IDs on path
Inputs:
- ctx: Context for cancellation. Must not be nil.
- u: Start node ID. Must exist in graph.
- v: End node ID. Must exist in graph.
Outputs:
- []string: Node IDs from u to v (inclusive). Nil on error.
- error: Non-nil if nodes don't exist or are in different trees.
Example:
path, err := hld.PathNodes(ctx, "funcA", "funcB")
if err != nil {
return fmt.Errorf("get path: %w", err)
}
fmt.Printf("Call path: %v\n", path)
Limitations:
- Allocates O(distance) memory
- Slower than DecomposePath for queries on large paths
- Not suitable for aggregate queries (use DecomposePath instead)
Thread Safety: Safe for concurrent use (allocates new slice).
func (*HLDecomposition) Pos ¶
func (hld *HLDecomposition) Pos(v int) int
Pos returns the DFS position of node v.
func (*HLDecomposition) SerializeMetadata ¶
func (hld *HLDecomposition) SerializeMetadata() map[string]string
SerializeMetadata returns HLD metadata for CRS TraceStep.
func (*HLDecomposition) Stats ¶
func (hld *HLDecomposition) Stats() HLDStats
Stats returns statistics about the HLD structure.
Description:
Computes: - Number of heavy paths - Average heavy path length - Maximum heavy path length - Light edge count Useful for debugging and performance analysis.
Thread Safety: Safe for concurrent use (read-only).
func (*HLDecomposition) SubtreeSize ¶
func (hld *HLDecomposition) SubtreeSize(v int) int
SubtreeSize returns the subtree size rooted at node v.
func (*HLDecomposition) Validate ¶
func (hld *HLDecomposition) Validate() error
Validate checks HLD invariants after construction.
Description:
Verifies: - All arrays have correct length - Root has parent -1 - Depths are valid (parent depth < child depth) - Subtree sizes are consistent - Heavy paths are well-formed - Position mapping is bijective
Complexity: O(V) time
Thread Safety: Safe for concurrent use (read-only).
func (*HLDecomposition) ValidateGraphHash ¶
func (hld *HLDecomposition) ValidateGraphHash(g *Graph) error
ValidateGraphHash checks if graph has changed since HLD construction.
Description:
Compares stored graph hash with current graph hash. Returns error if graph has changed, preventing stale results.
Inputs:
- g: Graph to validate against
Outputs:
- error: Non-nil if graph has changed or hash unavailable
Limitations:
- Requires HLD to have been built with graph hash
- Graph must provide Hash() method
Example:
if err := hld.ValidateGraphHash(graph); err != nil {
log.Warn("Graph changed, rebuilding HLD")
hld = BuildHLD(ctx, graph, root)
}
Thread Safety: Safe for concurrent use (read-only).
func (*HLDecomposition) Version ¶
func (hld *HLDecomposition) Version() int
Version returns the HLD schema version.
type HierarchicalGraph ¶
type HierarchicalGraph struct {
// Embedded graph provides all base functionality.
*Graph
// contains filtered or unexported fields
}
HierarchicalGraph wraps a Graph with hierarchical indexes for efficient package-level and file-level queries.
Description:
HierarchicalGraph embeds the base Graph and adds: - O(1) package lookup via packageIndex - O(1) file lookup via fileIndex - O(1) kind lookup via kindIndex - Cached cross-package edges - Package metadata with counts
Thread Safety:
After construction, HierarchicalGraph is safe for concurrent reads. Indexes are built during construction and never mutated.
Performance:
| Operation | Complexity | |-----------|------------| | GetNodesInPackage | O(1) lookup + O(k) copy | | GetNodesInFile | O(1) lookup + O(k) copy | | GetCrossPackageEdges | O(1) cached | | GetPackages | O(1) cached |
func WrapGraph ¶
func WrapGraph(g *Graph) (*HierarchicalGraph, error)
WrapGraph creates a HierarchicalGraph from an existing frozen Graph.
Description:
Wraps the given Graph and builds hierarchical indexes. The graph must be frozen (read-only) before wrapping.
Inputs:
g - The graph to wrap. Must be frozen (IsFrozen() == true).
Outputs:
*HierarchicalGraph - The wrapped graph with indexes. Never nil. error - Non-nil if the graph is not frozen.
Thread Safety:
The returned HierarchicalGraph is safe for concurrent reads.
func (*HierarchicalGraph) DrillDown ¶
func (hg *HierarchicalGraph) DrillDown(level int, id string) []*Node
DrillDown returns children at the next hierarchy level.
Description:
Starting from an entity at the given level, returns the entities one level deeper in the hierarchy. Level 0 (Project) → Level 1 (Packages): Returns all packages Level 1 (Package) → Level 2 (Files): Returns files in the package Level 2 (File) → Level 3 (Symbols): Returns symbols in the file Level 3 (Symbol): No children, returns empty
Inputs:
level - The current hierarchy level (0-3). id - The entity ID (empty for project, pkg path, file path, or node ID).
Outputs:
[]*Node - Child nodes at the next level. For levels 0-1, returns
representative nodes. For level 2, returns actual symbol nodes.
Thread Safety: Safe for concurrent use.
func (*HierarchicalGraph) DrillDownWithCRS ¶
func (hg *HierarchicalGraph) DrillDownWithCRS(ctx context.Context, level int, id string) ([]*Node, crs.TraceStep)
DrillDownWithCRS returns children and a TraceStep.
Inputs:
ctx - Context for cancellation. level - The current hierarchy level. id - The entity ID.
Outputs:
[]*Node - Child nodes at the next level. crs.TraceStep - TraceStep with navigation metadata.
Thread Safety: Safe for concurrent use.
func (*HierarchicalGraph) GetCrossPackageEdges ¶
func (hg *HierarchicalGraph) GetCrossPackageEdges() []*Edge
GetCrossPackageEdges returns all edges that cross package boundaries.
Description:
O(1) cached access, returns defensive copy of edge slice.
Outputs:
[]*Edge - Edges where source and target are in different packages.
Thread Safety: Safe for concurrent use.
func (*HierarchicalGraph) GetFilesInPackage ¶
func (hg *HierarchicalGraph) GetFilesInPackage(pkg string) []string
GetFilesInPackage returns all file paths in a package.
Inputs:
pkg - The package path to look up.
Outputs:
[]string - File paths in the package, empty slice if not found.
Thread Safety: Safe for concurrent use.
func (*HierarchicalGraph) GetInternalEdges ¶
func (hg *HierarchicalGraph) GetInternalEdges() []*Edge
GetInternalEdges returns all edges within the same package.
Description:
O(1) cached access, returns defensive copy of edge slice.
Outputs:
[]*Edge - Edges where source and target are in the same package.
Thread Safety: Safe for concurrent use.
func (*HierarchicalGraph) GetNodesByKind ¶
func (hg *HierarchicalGraph) GetNodesByKind(kind ast.SymbolKind) []*Node
GetNodesByKind returns all nodes of a specific kind.
Description:
O(1) lookup via kindIndex, returns defensive copy of node slice.
Inputs:
kind - The symbol kind to filter by.
Outputs:
[]*Node - Nodes of that kind, empty slice if none found.
Thread Safety: Safe for concurrent use.
func (*HierarchicalGraph) GetNodesInFile ¶
func (hg *HierarchicalGraph) GetNodesInFile(filePath string) []*Node
GetNodesInFile returns all nodes defined in a file.
Description:
O(1) lookup via fileIndex, returns defensive copy of node slice.
Inputs:
filePath - The file path to look up.
Outputs:
[]*Node - Nodes in the file, empty slice if not found.
Thread Safety: Safe for concurrent use.
func (*HierarchicalGraph) GetNodesInPackage ¶
func (hg *HierarchicalGraph) GetNodesInPackage(pkg string) []*Node
GetNodesInPackage returns all nodes in a package.
Description:
O(1) lookup via packageIndex, returns defensive copy of node slice.
Inputs:
pkg - The package path to look up.
Outputs:
[]*Node - Nodes in the package, empty slice if not found.
Thread Safety: Safe for concurrent use.
func (*HierarchicalGraph) GetNodesInPackageWithCRS ¶
func (hg *HierarchicalGraph) GetNodesInPackageWithCRS(ctx context.Context, pkg string) ([]*Node, crs.TraceStep)
GetNodesInPackageWithCRS returns nodes in a package and a TraceStep.
Description:
Same as GetNodesInPackage but also returns a TraceStep for CRS recording.
Inputs:
ctx - Context for cancellation (checked but not currently used). pkg - The package path to look up.
Outputs:
[]*Node - Nodes in the package. crs.TraceStep - TraceStep with query metadata.
Thread Safety: Safe for concurrent use.
func (*HierarchicalGraph) GetPackageDependencies ¶
func (hg *HierarchicalGraph) GetPackageDependencies(pkg string) []string
GetPackageDependencies returns packages that the given package imports.
Description:
Finds all packages that have IMPORTS edges from the given package, or CALLS/REFERENCES edges to symbols in other packages.
Inputs:
pkg - The package path to analyze.
Outputs:
[]string - Package paths that this package depends on.
Thread Safety: Safe for concurrent use.
func (*HierarchicalGraph) GetPackageDependents ¶
func (hg *HierarchicalGraph) GetPackageDependents(pkg string) []string
GetPackageDependents returns packages that import the given package.
Description:
Finds all packages that have incoming edges from the given package (reverse of GetPackageDependencies).
Inputs:
pkg - The package path to analyze.
Outputs:
[]string - Package paths that depend on this package.
Thread Safety: Safe for concurrent use.
func (*HierarchicalGraph) GetPackageInfo ¶
func (hg *HierarchicalGraph) GetPackageInfo(pkg string) *PackageInfo
GetPackageInfo returns metadata for a specific package.
Inputs:
pkg - The package path to look up.
Outputs:
*PackageInfo - Package metadata, or nil if not found.
Thread Safety: Safe for concurrent use.
func (*HierarchicalGraph) GetPackageNames ¶
func (hg *HierarchicalGraph) GetPackageNames() []string
GetPackageNames returns a sorted list of all package names.
Outputs:
[]string - Sorted package names.
Thread Safety: Safe for concurrent use.
func (*HierarchicalGraph) GetPackageSiblings ¶
func (hg *HierarchicalGraph) GetPackageSiblings(nodeID string) []string
GetPackageSiblings returns other files in the same package as the node.
Inputs:
nodeID - The node ID to find package siblings for.
Outputs:
[]string - File paths of sibling files (not including the node's file).
Thread Safety: Safe for concurrent use.
func (*HierarchicalGraph) GetPackages ¶
func (hg *HierarchicalGraph) GetPackages() []PackageInfo
GetPackages returns metadata for all packages in the graph.
Description:
Returns a sorted list of PackageInfo for all packages, including node counts, file counts, and coupling metrics.
Outputs:
[]PackageInfo - Package metadata, sorted by package name.
Thread Safety: Safe for concurrent use.
func (*HierarchicalGraph) GetPackagesWithCRS ¶
func (hg *HierarchicalGraph) GetPackagesWithCRS(ctx context.Context) ([]PackageInfo, crs.TraceStep)
GetPackagesWithCRS returns package info and a TraceStep.
Inputs:
ctx - Context for cancellation.
Outputs:
[]PackageInfo - Package metadata. crs.TraceStep - TraceStep with query metadata.
Thread Safety: Safe for concurrent use.
func (*HierarchicalGraph) GetSiblings ¶
func (hg *HierarchicalGraph) GetSiblings(nodeID string) []*Node
GetSiblings returns nodes at the same hierarchy level as the given node.
Description:
Returns other nodes that share the same parent. For a symbol, returns other symbols in the same file. For a file (identified by any symbol in it), returns other files in the same package.
Inputs:
nodeID - The node ID to find siblings for.
Outputs:
[]*Node - Sibling nodes (not including the input node itself).
Thread Safety: Safe for concurrent use.
func (*HierarchicalGraph) IsProductionFile ¶
func (hg *HierarchicalGraph) IsProductionFile(filePath string) bool
IsProductionFile returns true if the file is classified as production code.
Description:
If a FileClassification has been set (via SetFileClassification), delegates to fc.IsProduction(filePath). If no classification is set, falls back to heuristic checks: !isTestFilePath(filePath) && !isDocFilePath(filePath). This preserves existing behavior when the classification feature is not used.
Inputs:
filePath - The file path to check.
Outputs:
bool - True if the file is production code.
Thread Safety: Safe for concurrent use.
func (*HierarchicalGraph) RollUp ¶
func (hg *HierarchicalGraph) RollUp(nodeID string) (string, int)
RollUp returns the parent entity ID at the previous hierarchy level.
Description:
Given a node ID, returns the parent entity: Symbol → File path File → Package path Package → "" (project root) Project → "" (no parent)
Inputs:
nodeID - The node ID to get the parent for.
Outputs:
string - The parent entity ID, or empty string if at root. int - The parent's hierarchy level.
Thread Safety: Safe for concurrent use.
func (*HierarchicalGraph) SetFileClassification ¶
func (hg *HierarchicalGraph) SetFileClassification(fc *FileClassification)
SetFileClassification stores a computed file classification on the graph.
Description:
Called after ClassifyFiles() to attach the classification to the graph. Subsequent calls to IsProductionFile() will use this classification instead of the heuristic fallback.
Inputs:
fc - The file classification to store. May be nil (clears classification).
Thread Safety: NOT safe for concurrent use with IsProductionFile reads. Must be called during initialization (RegisterExploreTools) before any tool execution begins. After this call, IsProductionFile is safe for concurrent reads.
type HotspotNode ¶
type HotspotNode struct {
// Node is the graph node.
Node *Node
// Score is the connectivity score (higher = more connected).
// Computed as: inDegree*2 + outDegree*1
Score int
// InDegree is the number of incoming edges.
InDegree int
// OutDegree is the number of outgoing edges.
OutDegree int
}
HotspotNode represents a node with its connectivity score.
type IncrementalResult ¶
type IncrementalResult struct {
// Graph is the updated graph (frozen, read-only).
Graph *Graph
// Strategy is "incremental" or "full_rebuild".
Strategy string
// ChangedFiles is the list of files that were refreshed.
ChangedFiles []string
// NodesRemoved is the count of nodes removed from changed files.
NodesRemoved int
// NodesAdded is the count of nodes added from re-parsed files.
NodesAdded int
// EdgesCreated is the count of edges created during re-extraction.
EdgesCreated int
// DurationMilli is the total time for the incremental refresh.
DurationMilli int64
// FallbackReason is non-empty if a full rebuild was used instead.
FallbackReason string
// EnrichmentStats contains LSP enrichment results for the changed files.
// Zero-valued if LSP enrichment was not configured or not run.
// GR-76: Populated by Phase 6 when lspConfig is provided.
EnrichmentStats EnrichmentStats
}
IncrementalResult contains the outcome of an incremental graph refresh.
Thread Safety: Immutable after construction.
func IncrementalRefresh ¶
func IncrementalRefresh( ctx context.Context, baseGraph *Graph, changedFiles []string, changedResults []*ast.ParseResult, lspConfig *LSPEnrichmentConfig, ) (*IncrementalResult, error)
IncrementalRefresh applies changes to a previously-saved graph by removing nodes/edges for changed files and re-adding them from fresh parse results.
Description:
Given a base graph (from a prior snapshot) and parse results for changed files, this function: 1. Clones the base graph to get a writable copy 2. Removes all nodes and edges for each changed file 3. Adds new nodes from the parse results 4. Re-extracts per-file edges for the changed files 5. Freezes the updated graph Cross-file resolution (interface matching, Python import resolution) is NOT re-run. Existing edges from unchanged files are preserved via Clone. For changed files, per-file edges (imports, calls, returns) are re-extracted against the full symbol set. If structural changes affect cross-file edges (e.g., interface method additions, renamed exports), CRS-19 graph staleness detection catches the discrepancy.
Inputs:
- ctx: Context for cancellation and tracing. Must not be nil.
- baseGraph: The previous session's graph (frozen). Must not be nil.
- changedFiles: File paths (relative to project root) that changed.
- changedResults: Parse results for ONLY the changed files.
Outputs:
- *IncrementalResult: The result of the refresh operation.
- error: Non-nil if the operation fails.
Limitations:
- Cross-file edges FROM unchanged files TO renamed symbols are not updated.
- Interface implementation edges are not recomputed (use full rebuild for interface signature changes).
Thread Safety: Safe for concurrent use (creates a new graph via Clone).
type InheritanceQueryResult ¶
type InheritanceQueryResult struct {
// DirectCallers are callers of the primary (queried) method.
DirectCallers *QueryResult
// InheritedCallers maps parent method ID to callers of that parent method.
// Only includes callers NOT already in DirectCallers (deduplicated).
InheritedCallers map[string]*QueryResult
// Truncated is true if limit was reached or context was cancelled.
Truncated bool
// Duration is the total query execution time.
Duration time.Duration
}
InheritanceQueryResult separates direct callers from inherited callers.
Description:
Returned by FindCallersWithInheritance to allow the caller to distinguish between direct callers of the queried method and callers that reach it through parent class methods in the inheritance chain.
Example:
result, err := g.FindCallersWithInheritance(ctx, plotRenderID, parentIDs) // result.DirectCallers has callers of Plot.render // result.InheritedCallers["Component.render ID"] has callers of Component.render
func (*InheritanceQueryResult) AllCallers ¶
func (r *InheritanceQueryResult) AllCallers() *QueryResult
AllCallers returns a flat deduplicated list of all callers (direct + inherited).
Description:
Convenience method that merges DirectCallers and InheritedCallers into a single QueryResult. Useful when the caller doesn't need to distinguish between direct and inherited callers.
func (*InheritanceQueryResult) TotalCallerCount ¶
func (r *InheritanceQueryResult) TotalCallerCount() int
TotalCallerCount returns the total number of callers across all levels.
type IrreducibleRegion ¶
type IrreducibleRegion struct {
// ID is a unique 0-indexed identifier for this region.
ID int
// EntryNodes are the multiple entry points to this region.
// An irreducible region has >= 2 entry points.
EntryNodes []string
// Nodes contains all nodes in this irreducible region.
Nodes []string
// Size is the number of nodes in the region.
Size int
// CrossEdges are the edges that form this irreducible pattern.
CrossEdges [][2]string
// Reason explains why this region is irreducible.
Reason string
}
IrreducibleRegion represents a detected irreducible subgraph.
An irreducible region has multiple entry points - nodes that can be reached from outside the region via different paths.
Thread Safety: Safe for concurrent use after construction.
type LCAQueryEngine ¶
type LCAQueryEngine struct {
// contains filtered or unexported fields
}
LCAQueryEngine provides O(1) LCD queries after O(V log V) preprocessing.
Description:
Uses binary lifting to precompute 2^k-th ancestors for each node. This enables O(log depth) queries instead of O(depth), which is effectively O(1) for practical tree depths.
Thread Safety: Safe for concurrent use after construction.
func (*LCAQueryEngine) Query ¶
func (lca *LCAQueryEngine) Query(a, b string) string
Query returns LCD in O(log depth) time after preprocessing.
Description:
Uses the precomputed binary lifting table to find the LCD efficiently. First lifts the deeper node to the same depth, then binary searches for the LCD.
Inputs:
- a, b: Node IDs to query.
Outputs:
- string: The LCD node ID.
Thread Safety: Safe for concurrent use.
Complexity: O(log depth) per query.
type LCAStats ¶
type LCAStats struct {
QueryCount int64 // Total LCA queries (Unix milliseconds UTC)
AvgIterations float64 // Average iterations per query
MaxIterations int32 // Maximum iterations seen (int32 for atomic.CompareAndSwapInt32)
TotalDurationMs int64 // Total duration in milliseconds (Unix milliseconds per CLAUDE.md §4.6)
}
LCAStats tracks LCA query statistics.
Description:
Statistics are tracked atomically for thread-safe access. All durations stored as Unix milliseconds for consistency with CLAUDE.md §4.6.
Note on Consistency (A-H2):
Stats are eventually consistent. In high-concurrency scenarios, the four fields may be from slightly different snapshots. For precise snapshots, use external synchronization.
Thread Safety: Safe for concurrent access (atomic operations).
type LRUCache ¶
type LRUCache[K comparable, V any] struct { // contains filtered or unexported fields }
LRUCache is a thread-safe LRU cache with generics support.
Description:
Implements a fixed-size cache that evicts the least recently used entries when capacity is reached. Uses container/list for O(1) access and eviction.
Thread Safety: All methods are safe for concurrent use.
Performance:
| Operation | Complexity | |-----------|------------| | Get | O(1) | | Set | O(1) | | Delete | O(1) | | Purge | O(n) |
func NewLRUCache ¶
func NewLRUCache[K comparable, V any](capacity int) *LRUCache[K, V]
NewLRUCache creates a new LRU cache with the given capacity.
Description:
Creates a fixed-size LRU cache. When the cache is full, the least recently accessed entry is evicted to make room for new entries.
Inputs:
- capacity: Maximum number of entries. Must be > 0.
Outputs:
- *LRUCache[K, V]: The cache. Never nil.
Example:
cache := NewLRUCache[string, *QueryResult](1000)
cache.Set("key", result)
if val, ok := cache.Get("key"); ok {
// use val
}
Thread Safety: The returned cache is safe for concurrent use.
func (*LRUCache[K, V]) Delete ¶
Delete removes a key from the cache.
Inputs:
- key: The key to remove.
Outputs:
- bool: True if the key was found and removed.
Thread Safety: Safe for concurrent use.
func (*LRUCache[K, V]) Evictions ¶
Evictions returns the number of cache evictions since creation or last purge.
Description:
GR-10 Review (O-1): Tracks evictions for monitoring cache effectiveness. High eviction count relative to cache size indicates capacity may be too small.
Outputs:
- evictions: Number of entries evicted due to capacity limits.
Thread Safety: Safe for concurrent use (lock-free).
func (*LRUCache[K, V]) Get ¶
Get retrieves a value from the cache.
Description:
Returns the value associated with the key and moves it to the front of the LRU list (most recently used).
Inputs:
- key: The key to look up.
Outputs:
- V: The value (zero value if not found).
- bool: True if the key was found.
Thread Safety: Safe for concurrent use.
func (*LRUCache[K, V]) Len ¶
Len returns the number of entries in the cache.
Thread Safety: Safe for concurrent use.
func (*LRUCache[K, V]) Purge ¶
func (c *LRUCache[K, V]) Purge()
Purge clears all entries from the cache.
Description:
Removes all entries and resets hit/miss/eviction counters.
Thread Safety: Safe for concurrent use.
func (*LRUCache[K, V]) Set ¶
func (c *LRUCache[K, V]) Set(key K, value V)
Set adds or updates a value in the cache.
Description:
Adds the key-value pair to the cache. If the key exists, updates the value and moves it to the front. If the cache is full, evicts the least recently used entry.
Inputs:
- key: The key to store.
- value: The value to associate with the key.
Thread Safety: Safe for concurrent use.
type LSPEnrichmentConfig ¶
type LSPEnrichmentConfig struct {
// Querier provides LSP operations. Required — enrichment is skipped if nil.
Querier LSPQuerier
// Manager provides IsAvailable checks. Optional — if nil, all languages are attempted.
Manager *lsp.Manager
// MaxConcurrentFiles is the maximum number of files processed in parallel.
// Default: 4
MaxConcurrentFiles int
// PerFileTimeout is the maximum time to process a single file's enrichment queries.
// Default: 30s
PerFileTimeout time.Duration
// TotalTimeout is the maximum time for the entire enrichment phase.
// Default: 120s
TotalTimeout time.Duration
// Languages is the list of languages to enrich. Placeholders from other languages
// are skipped. Default: ["python", "typescript", "javascript"]
// Note: JavaScript uses the same typescript-language-server binary as TypeScript.
Languages []string
// FileReader overrides the default os.ReadFile for reading source files.
// If nil, os.ReadFile is used. Provided for testability.
FileReader func(path string) ([]byte, error)
}
LSPEnrichmentConfig configures the LSP enrichment phase.
Description:
GR-74: Controls which LSP server to query, timeouts, parallelism, and which languages to enrich. When added to BuilderOptions via WithLSPEnrichment, the builder runs an enrichment phase after edge extraction.
Thread Safety:
Immutable after construction.
type LSPQuerier ¶
type LSPQuerier interface {
// OpenDocument notifies the LSP server that a document was opened.
//
// Inputs:
//
// ctx - Context for cancellation and timeout
// filePath - Absolute path to the file
// content - The file content
//
// Outputs:
//
// error - Non-nil on failure
OpenDocument(ctx context.Context, filePath, content string) error
// CloseDocument notifies the LSP server that a document was closed.
//
// Inputs:
//
// ctx - Context for cancellation and timeout
// filePath - Absolute path to the file
//
// Outputs:
//
// error - Non-nil on failure
CloseDocument(ctx context.Context, filePath string) error
// Definition returns the definition location(s) for a symbol.
//
// Inputs:
//
// ctx - Context for cancellation and timeout
// filePath - Absolute path to the file
// line - 1-indexed line number
// col - 0-indexed column number
//
// Outputs:
//
// []lsp.Location - Definition location(s)
// error - Non-nil on failure
Definition(ctx context.Context, filePath string, line, col int) ([]lsp.Location, error)
}
LSPQuerier abstracts LSP operations for testability.
Description:
GR-74: The graph builder uses this interface instead of calling lsp.Operations directly. This allows tests to provide mock implementations that return deterministic results without requiring actual LSP servers.
Thread Safety:
Implementations must be safe for concurrent use from multiple goroutines.
type LeidenOptions ¶
type LeidenOptions struct {
// MaxIterations limits total outer loop passes. Default: 100
MaxIterations int
// ConvergenceThreshold stops early if modularity gain < this. Default: 1e-6
ConvergenceThreshold float64
// MinCommunitySize filters out tiny communities from results. Default: 1
MinCommunitySize int
// Resolution affects community granularity. Default: 1.0
// Higher values produce smaller, more granular communities.
// Lower values produce larger, coarser communities.
Resolution float64
}
LeidenOptions configures the Leiden algorithm.
func DefaultLeidenOptions ¶
func DefaultLeidenOptions() *LeidenOptions
DefaultLeidenOptions returns sensible defaults.
func (*LeidenOptions) Validate ¶
func (o *LeidenOptions) Validate()
Validate checks options and applies defaults for invalid values.
type Loop ¶
type Loop struct {
// Header is the loop header node ID (the dominator).
// The header is the single entry point to the loop.
Header string
// BackEdges contains edges that form this loop.
// Each entry is [fromID, toID] where toID is the header.
BackEdges [][2]string
// Body contains all node IDs in the loop body (including header).
// Computed via reverse BFS from back edge sources.
Body []string
// Size is the number of nodes in the loop body.
Size int
// Depth is the nesting depth (0 = top-level loop).
Depth int
// Parent is the enclosing loop (nil if top-level).
Parent *Loop
// Children are nested loops within this loop.
Children []*Loop
}
Loop represents a natural loop in the graph.
A natural loop is defined by a back edge A→B where B dominates A. The header B is the single entry point to the loop.
Thread Safety: Safe for concurrent use after construction.
type LoopDetectionError ¶
type LoopDetectionError struct {
Message string
}
LoopDetectionError represents an error in loop detection.
func (*LoopDetectionError) Error ¶
func (e *LoopDetectionError) Error() string
type LoopNest ¶
type LoopNest struct {
// Loops contains all detected loops.
Loops []*Loop
// TopLevel contains loops with no parent.
TopLevel []*Loop
// LoopOf maps node ID → innermost containing loop.
LoopOf map[string]*Loop
// MaxDepth is the maximum nesting depth.
MaxDepth int
// BackEdgeCount is the total number of back edges found.
BackEdgeCount int
// DomTree is the dominator tree used for detection.
DomTree *DominatorTree
}
LoopNest represents the complete loop nesting structure.
Thread Safety: Safe for concurrent use after construction.
func (*LoopNest) GetInnermostLoop ¶
GetInnermostLoop returns the innermost loop containing a node.
Description:
Returns the most deeply nested loop that contains the specified node. For nested loops, this returns the inner loop, not the outer one.
Inputs:
- nodeID: The node to query. Must be a valid node ID.
Outputs:
- *Loop: The innermost containing loop, or nil if not in any loop.
Limitations:
- Returns nil if receiver is nil or nodeID is not in any loop.
Thread Safety: Safe for concurrent use.
Complexity: O(1) map lookup.
func (*LoopNest) GetLoopByHeader ¶
GetLoopByHeader returns the loop with the specified header.
Description:
Finds the loop whose header matches the specified node ID. Each loop has exactly one header.
Inputs:
- headerID: The header node ID to search for.
Outputs:
- *Loop: The loop with this header, or nil if no such loop exists.
Limitations:
- Returns nil if receiver is nil or no loop has this header.
- Linear search through loops.
Thread Safety: Safe for concurrent use.
Complexity: O(loops) linear search.
func (*LoopNest) IsInLoop ¶
IsInLoop checks if a node is inside any loop.
Description:
Returns true if the specified node is part of any detected loop body. This includes loop headers and all nodes within loop bodies.
Inputs:
- nodeID: The node to check. Must be a valid node ID.
Outputs:
- bool: True if the node is in any loop, false otherwise.
Limitations:
- Returns false if receiver is nil or nodeID is not in any loop.
Thread Safety: Safe for concurrent use.
Complexity: O(1) map lookup.
func (*LoopNest) IsLoopHeader ¶
IsLoopHeader returns true if the node is a loop header.
Description:
Checks if the specified node is the header (entry point) of any loop.
Inputs:
- nodeID: The node to check. Must be a valid node ID.
Outputs:
- bool: True if the node is a loop header, false otherwise.
Limitations:
- Returns false if receiver is nil.
Thread Safety: Safe for concurrent use.
Complexity: O(loops) linear search.
type NoOpCache ¶
type NoOpCache struct{}
NoOpCache is a cache that does nothing (always misses). Used as default when no cache is configured.
func (NoOpCache) Invalidate ¶
func (NoOpCache) Invalidate()
func (NoOpCache) SetDistance ¶
func (NoOpCache) SetPath ¶
func (NoOpCache) SetPath(u, v string, segments []PathSegment, ttl time.Duration)
func (NoOpCache) Stats ¶
func (NoOpCache) Stats() CacheStats
type NoOpCircuitBreaker ¶
type NoOpCircuitBreaker struct{}
NoOpCircuitBreaker never opens the circuit. Used as default when no circuit breaker is configured.
func (NoOpCircuitBreaker) Execute ¶
func (NoOpCircuitBreaker) Execute(fn func() error) error
func (NoOpCircuitBreaker) State ¶
func (NoOpCircuitBreaker) State() CircuitState
func (NoOpCircuitBreaker) Stats ¶
func (NoOpCircuitBreaker) Stats() CircuitBreakerStats
type NoOpRateLimiter ¶
type NoOpRateLimiter struct{}
NoOpRateLimiter always allows all queries. Used as default when no rate limiting is configured.
func (NoOpRateLimiter) Allow ¶
func (NoOpRateLimiter) Allow() bool
func (NoOpRateLimiter) Stats ¶
func (NoOpRateLimiter) Stats() RateLimiterStats
type Node ¶
type Node struct {
// ID is the unique identifier, same as Symbol.ID.
ID string
// Symbol is the underlying symbol from AST parsing.
// This pointer is NOT owned by the Node.
Symbol *ast.Symbol
// Outgoing contains edges where this node is the source.
// For example, if this node is a function, Outgoing contains
// all the functions it calls.
Outgoing []*Edge
// Incoming contains edges where this node is the target.
// For example, if this node is a function, Incoming contains
// all the functions that call it.
Incoming []*Edge
}
Node represents a symbol in the code graph with its relationships.
The Symbol pointer is NOT owned by the Node. The referenced Symbol MUST NOT be mutated after the Node is added to a Graph.
type NodeDiff ¶
type NodeDiff struct {
// NodeID is the unique node identifier.
NodeID string `json:"node_id"`
// SymbolName is the human-readable name of the symbol.
SymbolName string `json:"symbol_name"`
// ChangeType describes what changed: "signature_changed", "moved", "edges_changed".
ChangeType string `json:"change_type"`
}
NodeDiff describes how a single node changed between snapshots.
type PackageInfo ¶
type PackageInfo struct {
// Name is the package path (e.g., "services/trace/graph").
Name string
// Files lists file paths in this package.
Files []string
// NodeCount is the total number of symbols in this package.
NodeCount int
// ExportedCount is the number of exported (public) symbols.
ExportedCount int
// ImportCount is the number of packages this package imports.
ImportCount int
// ImportedByCount is the number of packages that import this package.
ImportedByCount int
// Types counts types (struct, interface, etc.) in the package.
Types int
// Functions counts functions and methods in the package.
Functions int
}
PackageInfo contains metadata about a package in the graph.
type PageRankNode ¶
type PageRankNode struct {
// Node is the graph node.
Node *Node
// Score is the PageRank score.
Score float64
// Rank is the position in the ranking (1-indexed).
Rank int
// DegreeScore is the simple degree-based score for comparison.
// Computed as: inDegree*2 + outDegree
DegreeScore int
}
PageRankNode represents a node with its PageRank score and rank.
type PageRankOptions ¶
type PageRankOptions struct {
// DampingFactor is the probability of following a link (vs random jump).
// Must be in [0, 1]. Default: 0.85
DampingFactor float64
// MaxIterations is the maximum iterations before stopping.
// Must be > 0. Default: 100
MaxIterations int
// Convergence is the threshold for convergence detection.
// Must be > 0. Default: 1e-6
Convergence float64
}
PageRankOptions configures the PageRank algorithm.
func DefaultPageRankOptions ¶
func DefaultPageRankOptions() *PageRankOptions
DefaultPageRankOptions returns sensible defaults.
func (*PageRankOptions) Validate ¶
func (o *PageRankOptions) Validate()
Validate checks options and applies defaults for invalid values.
type PageRankResult ¶
type PageRankResult struct {
// Scores maps nodeID to PageRank score.
// Scores sum to approximately 1.0.
Scores map[string]float64
// Iterations is the actual number of iterations performed.
Iterations int
// Converged indicates whether the algorithm converged before MaxIterations.
Converged bool
// MaxDiff is the final maximum score difference (useful for debugging).
MaxDiff float64
}
PageRankResult contains the output of PageRank computation.
type PathQueryEngine ¶
type PathQueryEngine struct {
// contains filtered or unexported fields
}
PathQueryEngine combines HLD and Segment Tree for efficient path aggregate queries.
Description:
Provides O(log² V) path aggregate queries by decomposing paths into O(log V) heavy path segments and querying each segment with a segment tree. Supports SUM, MIN, MAX, GCD aggregations over node values.
Invariants:
- Exactly one of hld or forest must be non-nil
- HLD and SegmentTree must be built from same graph
- Node indices must align between HLD positions and segment tree array
- SegmentTree aggregation function must match query type
- In forest mode, path queries only valid within single tree component
Thread Safety:
Safe for concurrent queries (read-only operations). Stats are tracked with atomic operations.
func NewPathQueryEngine ¶
func NewPathQueryEngine(hld *HLDecomposition, segTree *SegmentTree, aggFunc AggregateFunc, opts *PathQueryEngineOptions) (*PathQueryEngine, error)
NewPathQueryEngine creates a path query engine for single-tree graphs.
Description:
Combines HLD and segment tree to enable efficient path queries. The segment tree must be built over node values in HLD position order.
Algorithm:
Time: O(1) - just validates and stores references Space: O(C) where C is total cache capacity
Inputs:
- hld: Heavy-Light Decomposition. Must not be nil.
- segTree: Segment tree over node values. Must not be nil.
- aggFunc: Aggregation function. Must match segTree's aggregation.
- opts: Configuration options. If nil, uses defaults.
Outputs:
- *PathQueryEngine: Ready for queries. Never nil on success.
- error: Non-nil if inputs are invalid.
Example:
// Build value array in HLD position order
values := make([]int64, hld.NodeCount())
for i := 0; i < hld.NodeCount(); i++ {
nodeID := hld.NodeAtPosition(i)
values[i] = getNodeValue(nodeID)
}
// Build segment tree
segTree, _ := NewSegmentTree(ctx, values, AggregateSUM)
// Create query engine
opts := DefaultPathQueryEngineOptions()
engine, _ := NewPathQueryEngine(hld, segTree, AggregateSUM, &opts)
Limitations:
- Only works with single-tree graphs (no forests)
- Cannot change aggregation function after creation
- Cache sizes fixed at creation time
Assumptions:
- Values array used for segment tree corresponds to HLD position array
- Graph structure unchanged since HLD construction (validated by graph hash)
- All node values fit in int64
Thread Safety: The returned engine is safe for concurrent use.
func NewPathQueryEngineForForest ¶
func NewPathQueryEngineForForest(forest *HLDForest, segTree *SegmentTree, aggFunc AggregateFunc, opts *PathQueryEngineOptions) (*PathQueryEngine, error)
NewPathQueryEngineForForest creates a path query engine for multi-tree graphs (forests).
Description:
Like NewPathQueryEngine but supports forests with multiple disconnected trees. Path queries are only valid within a single tree component.
Inputs:
- forest: HLD forest structure. Must not be nil.
- segTree: Segment tree over all node values. Must not be nil.
- aggFunc: Aggregation function. Must match segTree's aggregation.
- opts: Configuration options. If nil, uses defaults.
Outputs:
- *PathQueryEngine: Ready for queries. Never nil on success.
- error: Non-nil if inputs are invalid.
Example:
forest, _ := BuildHLDForest(ctx, graph) values := buildValueArrayForForest(forest) segTree, _ := NewSegmentTree(ctx, values, AggregateSUM) engine, _ := NewPathQueryEngineForForest(forest, segTree, AggregateSUM, nil)
Limitations:
- Path queries between nodes in different trees will return an error
- Cannot query across tree boundaries even if graph has edges between components
Assumptions:
- Forest and segment tree built from same graph
- Values array corresponds to forest's unified position array
Thread Safety: The returned engine is safe for concurrent use.
func NewPathQueryEngineWithCRS ¶
func NewPathQueryEngineWithCRS(hld *HLDecomposition, segTree *SegmentTree, aggFunc AggregateFunc, opts *PathQueryEngineOptions, crsInstance crs.CRS, sessionID string) (*PathQueryEngine, error)
NewPathQueryEngineWithCRS creates a path query engine with optional CRS integration.
Description:
Like NewPathQueryEngine but accepts optional CRS instance and session ID for recording sub-steps (LCA computation, path decomposition). If crsInstance is nil, CRS recording is disabled.
Inputs:
- hld: HLD structure. Must not be nil.
- segTree: Segment tree for range queries. Must not be nil.
- aggFunc: Aggregation function (SUM/MIN/MAX/GCD). Must be valid constant.
- opts: Configuration options. If nil, uses defaults.
- crsInstance: Optional CRS for sub-step recording. Can be nil.
- sessionID: CRS session ID. Ignored if crsInstance is nil.
Outputs:
- *PathQueryEngine: Configured engine ready for queries.
- error: Non-nil if validation fails.
Limitations:
- Single-tree graphs only (use NewPathQueryEngineForForest for forests)
- CRS recording adds ~0.1-0.5ms overhead per query
Assumptions:
- hld and segTree are compatible (same node count, same agg func)
- Caller validates crsInstance is not nil if sessionID is provided
Thread Safety: The returned engine is safe for concurrent use.
func (*PathQueryEngine) PathGCD ¶
func (pqe *PathQueryEngine) PathGCD(ctx context.Context, u, v string, logger *slog.Logger) (int64, error)
PathGCD computes GCD of values on path from u to v.
Description:
Convenience wrapper for PathQuery with GCD aggregation. Requires engine to be configured with AggregateGCD.
Inputs:
- ctx: Context for cancellation. Must not be nil.
- u: Start node ID. Must exist in graph.
- v: End node ID. Must exist in graph.
- logger: Logger for structured logging. Must not be nil.
Outputs:
- int64: GCD of values along path u → v.
- error: Non-nil if engine not configured for GCD or PathQuery fails.
Example:
gcdVersion, err := engine.PathGCD(ctx, "main", "leaf", logger)
Limitations:
- Engine must be created with AggregateGCD
- Nodes must be in same tree (forest mode)
- GCD undefined for paths with zero values
Assumptions:
- Caller has validated engine configuration
- Node IDs are valid and exist in graph
- All values on path are non-negative
Thread Safety: Safe for concurrent use.
func (*PathQueryEngine) PathMax ¶
func (pqe *PathQueryEngine) PathMax(ctx context.Context, u, v string, logger *slog.Logger) (int64, error)
PathMax computes maximum value on path from u to v.
Description:
Convenience wrapper for PathQuery with MAX aggregation. Requires engine to be configured with AggregateMAX.
Inputs:
- ctx: Context for cancellation. Must not be nil.
- u: Start node ID. Must exist in graph.
- v: End node ID. Must exist in graph.
- logger: Logger for structured logging. Must not be nil.
Outputs:
- int64: Maximum value along path u → v.
- error: Non-nil if engine not configured for MAX or PathQuery fails.
Example:
maxLatency, err := engine.PathMax(ctx, "main", "leaf", logger)
Limitations:
- Engine must be created with AggregateMAX
- Nodes must be in same tree (forest mode)
Assumptions:
- Caller has validated engine configuration
- Node IDs are valid and exist in graph
Thread Safety: Safe for concurrent use.
func (*PathQueryEngine) PathMin ¶
func (pqe *PathQueryEngine) PathMin(ctx context.Context, u, v string, logger *slog.Logger) (int64, error)
PathMin computes minimum value on path from u to v.
Description:
Convenience wrapper for PathQuery with MIN aggregation. Requires engine to be configured with AggregateMIN.
Inputs:
- ctx: Context for cancellation. Must not be nil.
- u: Start node ID. Must exist in graph.
- v: End node ID. Must exist in graph.
- logger: Logger for structured logging. Must not be nil.
Outputs:
- int64: Minimum value along path u → v.
- error: Non-nil if engine not configured for MIN or PathQuery fails.
Example:
minMemory, err := engine.PathMin(ctx, "main", "leaf", logger)
Limitations:
- Engine must be created with AggregateMIN
- Nodes must be in same tree (forest mode)
Assumptions:
- Caller has validated engine configuration
- Node IDs are valid and exist in graph
Thread Safety: Safe for concurrent use.
func (*PathQueryEngine) PathQuery ¶
func (pqe *PathQueryEngine) PathQuery(ctx context.Context, u, v string, logger *slog.Logger) (int64, error)
PathQuery computes aggregate over path from u to v.
Description:
Decomposes path into heavy path segments, queries each segment with segment tree, and combines results using aggregation function. Handles LCA double-counting for SUM (but not MIN/MAX - idempotent).
Algorithm:
Time: O(log² V) = O(log V) segments × O(log V) per query Space: O(log V) - store segment query results
Inputs:
- ctx: Context for cancellation and timeout. Must not be nil.
- u: Start node ID. Must exist in graph and not be empty.
- v: End node ID. Must exist in graph and not be empty.
- logger: Structured logger for events. Must not be nil.
Outputs:
- int64: Aggregate value over path u → v.
- error: Non-nil if nodes don't exist, cross-tree in forest, or query fails.
Special Cases:
- PathQuery(u, u) = value[u]
- PathQuery(u, parent[u]) = aggFunc(value[u], value[parent[u]])
Example:
sum, err := engine.PathQuery(ctx, "main", "helper", logger)
if err != nil {
return fmt.Errorf("path query: %w", err)
}
fmt.Printf("Total complexity on path: %d\n", sum)
Limitations:
- Only works within single tree component (error for cross-tree in forest)
- Requires graph unchanged since HLD construction (checked via graph hash)
- Maximum depth limited by opts.MaxTreeDepth
Assumptions:
- Segment tree values correspond to HLD position array
- Node values fit in int64
- Caller has validated graph hasn't changed
Thread Safety: Safe for concurrent use.
func (*PathQueryEngine) PathQueryCacheKey ¶
func (pqe *PathQueryEngine) PathQueryCacheKey(u, v string) string
PathQueryCacheKey generates deterministic cache key for path query.
Description:
H2: Includes graph hash to ensure cache invalidation on graph changes. Canonical ordering for symmetric aggregations (MIN/MAX). Uses strings.Builder for efficient string concatenation.
Inputs:
- u: Start node ID. Must not be empty.
- v: End node ID. Must not be empty.
Outputs:
- string: Cache key in format "pathquery:{graphHash}:{u}:{v}:{aggFunc}"
Limitations:
- Keys are unique per (graph, u, v, aggFunc) tuple
- MIN/MAX queries use canonical ordering (smaller node first)
Assumptions:
- Graph hash is stable for same graph structure
- Node IDs don't contain ':' character
Thread Safety: Safe for concurrent use (read-only).
func (*PathQueryEngine) PathSum ¶
func (pqe *PathQueryEngine) PathSum(ctx context.Context, u, v string, logger *slog.Logger) (int64, error)
PathSum computes sum of values on path from u to v.
Description:
Convenience wrapper for PathQuery with SUM aggregation. Requires engine to be configured with AggregateSUM.
Inputs:
- ctx: Context for cancellation. Must not be nil.
- u: Start node ID. Must exist in graph.
- v: End node ID. Must exist in graph.
- logger: Logger for structured logging. Must not be nil.
Outputs:
- int64: Sum of values along path u → v.
- error: Non-nil if engine not configured for SUM or PathQuery fails.
Example:
totalComplexity, err := engine.PathSum(ctx, "main", "leaf", logger)
Limitations:
- Engine must be created with AggregateSUM
- Nodes must be in same tree (forest mode)
Assumptions:
- Caller has validated engine configuration
- Node IDs are valid and exist in graph
Thread Safety: Safe for concurrent use.
func (*PathQueryEngine) Stats ¶
func (pqe *PathQueryEngine) Stats() PathQueryStats
Stats returns query statistics.
Description:
C2: Thread-safe stats retrieval using atomic operations. Returns snapshot of current statistics. M-IMPL-2: Includes SegmentsPerQuery average.
Inputs:
- None
Outputs:
- PathQueryStats: Snapshot of statistics including query count, latency, cache hit ratio, segments per query
Limitations:
- Snapshot may be stale immediately after return
- SegmentsPerQuery excludes cache hits (counted as 0 segments)
- CacheHitRatio includes both LCA cache and query cache
Assumptions:
- Atomic loads provide consistent point-in-time snapshot
- QueryCount > 0 when calculating averages
Thread Safety: Safe for concurrent use (atomic reads).
func (*PathQueryEngine) Validate ¶
func (pqe *PathQueryEngine) Validate() error
Validate checks that HLD and SegmentTree are compatible and valid.
Description:
Verifies: - HLD or Forest is valid - SegmentTree is valid - Size compatibility - Aggregation function consistency L-IMPL-2: Creates OpenTelemetry span to track validation.
Inputs:
- None (validates internal state)
Outputs:
- error: Non-nil if validation fails with detailed error message
Limitations:
- Only checks structural validity, not semantic correctness
- Does not validate node values or tree structure
Assumptions:
- Engine fields are immutable after construction
- HLD and SegmentTree built from same graph
Thread Safety: Safe for concurrent use (read-only).
type PathQueryEngineOptions ¶
type PathQueryEngineOptions struct {
// EnableLCACache enables caching of LCA results.
// Default: true
EnableLCACache bool
// LCACacheSize sets maximum number of LCA results to cache.
// Default: 10000
LCACacheSize int
// EnableQueryCache enables caching of full path query results.
// Default: false (disabled by default as results may be large)
EnableQueryCache bool
// QueryCacheSize sets maximum number of query results to cache.
// Default: 1000
QueryCacheSize int
// QueryTimeout sets maximum time allowed for a single path query.
// Default: 30 seconds
QueryTimeout time.Duration
// MaxTreeDepth sets maximum allowed tree depth to prevent OOM.
// Default: 10000
MaxTreeDepth int
// SlowQueryThreshold defines the duration threshold for slow query warnings.
// Queries exceeding this threshold will emit a warning log.
// Default: 5 seconds
SlowQueryThreshold time.Duration
}
PathQueryEngineOptions configures path query engine behavior.
Description:
Controls caching, timeouts, and resource limits for path query operations. All fields are optional with sensible defaults.
Thread Safety: Immutable after creation, safe to share across goroutines.
func DefaultPathQueryEngineOptions ¶
func DefaultPathQueryEngineOptions() PathQueryEngineOptions
DefaultPathQueryEngineOptions returns default options.
type PathQueryStats ¶
type PathQueryStats struct {
QueryCount int64 // Total queries
TotalLatency time.Duration // Sum of all query durations
AvgLatency time.Duration // Average query duration
LastQueryTime int64 // Unix milliseconds UTC of last query
SegmentsPerQuery float64 // Average segments per query (requires tracking)
CacheHitRatio float64 // Ratio of cache hits to total lookups
}
PathQueryStats contains query statistics.
Description:
Statistics collected across all queries on this engine. All timestamps in Unix milliseconds UTC per CLAUDE.md Section 4.6.
Thread Safety: Snapshot at time of Stats() call, may be stale.
type PathResult ¶
type PathResult struct {
// From is the starting node ID.
From string
// To is the target node ID.
To string
// Path contains node IDs in path order, including From and To.
// Empty if no path exists.
Path []string
// Length is the number of edges in the path.
// -1 if no path exists.
Length int
}
PathResult contains the result of a shortest path query.
type PathSegment ¶
type PathSegment struct {
Start int // Start position (inclusive)
End int // End position (inclusive)
IsUpward bool // True if this segment goes upward (child to parent)
}
PathSegment represents a contiguous segment of a path in HLD position space.
Description:
A path segment covers positions [Start, End] (inclusive) in the HLD position array. The segment corresponds to a portion of a heavy path. All positions in the segment are guaranteed to be on the same heavy path.
Invariants:
- Start >= 0 and End >= 0
- Start, End in [0, nodeCount)
- All positions in [Start, End] are on same heavy path
- IsUpward indicates direction: true = child→parent, false = parent→child
Usage:
Used as input to segment tree range queries. For path query(u, v), decompose into O(log V) PathSegments, query each with segment tree.
Thread Safety: Immutable after creation, safe for concurrent use.
type PathUpdateEngine ¶
type PathUpdateEngine struct {
*PathQueryEngine // Embed for CRS fields (H-CRS-1)
// contains filtered or unexported fields
}
PathUpdateEngine provides path update operations using HLD and segment tree.
Description:
Extends PathQueryEngine with update operations. Updates values along paths by decomposing into O(log V) segments and applying range updates with lazy propagation for O(log² V) total time. Supports forest mode - updates only within single tree component.
Invariants:
- Segment tree must support range updates (RangeUpdate method)
- Only works with SUM aggregation (GR-19b restriction)
- Updates invalidate query cache for correctness
- In forest mode, path updates only valid within single tree component
- Segment tree version increments on each update
Thread Safety:
NOT safe for concurrent updates (PathUpdate, PathSet, etc.). NOT safe for concurrent update+query (segment tree Query takes write lock for lazy push). Safe for concurrent queries only (no updates in progress). Caller MUST use external synchronization (e.g., sync.RWMutex): - Lock.RLock() for queries - Lock.Lock() for updates
func NewPathUpdateEngine ¶
func NewPathUpdateEngine(pqe *PathQueryEngine) (*PathUpdateEngine, error)
NewPathUpdateEngine creates a path update engine.
Description:
Wraps PathQueryEngine to add update capabilities. Requires segment tree to support RangeUpdate (SUM aggregation only). Validates aggregation function at construction time.
Algorithm:
Time: O(1) - wraps existing engine Space: O(1) - no allocations
Inputs:
- pqe: Path query engine. Must not be nil.
Outputs:
- *PathUpdateEngine: Ready for updates. Never nil on success.
- error: Non-nil if query engine is nil, invalid, or not configured for SUM.
Example:
// Build value array in HLD position order
values := make([]int64, hld.NodeCount())
for i := 0; i < hld.NodeCount(); i++ {
nodeID := hld.NodeAtPosition(i)
values[i] = getNodeValue(nodeID)
}
// Build segment tree with SUM aggregation
segTree, _ := NewSegmentTree(ctx, values, AggregateSUM)
// Create query engine
queryEngine, _ := NewPathQueryEngine(hld, segTree, AggregateSUM, nil)
// Create update engine
updateEngine, _ := NewPathUpdateEngine(queryEngine)
Limitations:
- Only works with SUM aggregation (GR-19b RangeUpdate restriction)
- MIN/MAX/GCD engines cannot be used for updates
- Constructor validates aggregation function and returns error if not SUM
Assumptions:
- Query engine's segment tree was built with SUM aggregation
- Caller has validated compatibility between HLD and segment tree
Thread Safety: Safe for concurrent use (wraps read-only engine).
func (*PathUpdateEngine) PathDecrement ¶
func (pue *PathUpdateEngine) PathDecrement(ctx context.Context, u, v string, logger *slog.Logger) error
PathDecrement decrements all values on path (delta=-1).
Description:
Convenience wrapper for PathUpdate with delta=-1.
Inputs:
- ctx: Context for cancellation. Must not be nil.
- u: Start node ID. Must exist in graph.
- v: End node ID. Must exist in graph.
- logger: Structured logger for events. Must not be nil.
Outputs:
- error: Non-nil if update fails.
Thread Safety: NOT safe for concurrent use.
func (*PathUpdateEngine) PathIncrement ¶
func (pue *PathUpdateEngine) PathIncrement(ctx context.Context, u, v string, logger *slog.Logger) error
PathIncrement increments all values on path (delta=+1).
Description:
Convenience wrapper for PathUpdate with delta=+1.
Inputs:
- ctx: Context for cancellation. Must not be nil.
- u: Start node ID. Must exist in graph.
- v: End node ID. Must exist in graph.
- logger: Structured logger for events. Must not be nil.
Outputs:
- error: Non-nil if update fails.
Thread Safety: NOT safe for concurrent use.
func (*PathUpdateEngine) PathSet ¶
func (pue *PathUpdateEngine) PathSet(ctx context.Context, u, v string, value int64, logger *slog.Logger) error
PathSet sets all node values on path from u to v to a specific value.
Description:
Sets each node on path to value. Implemented by computing current aggregate and calculating delta needed to achieve target value. Less efficient than PathUpdate (requires querying current values).
Algorithm:
Time: O(log² V) query + O(log² V) update = O(log² V) Space: O(log V)
Inputs:
- ctx: Context for cancellation. Must not be nil.
- u: Start node ID. Must exist in graph.
- v: End node ID. Must exist in graph.
- value: New value for all nodes on path.
- logger: Structured logger for events. Must not be nil.
Outputs:
- error: Non-nil if operation fails.
Example:
// Set all complexity values on path to 10 err := engine.PathSet(ctx, "main", "leaf", 10, logger)
Limitations:
- Requires two passes: query current aggregate, then update
- Only works with SUM aggregation
- NOT safe for concurrent updates
Assumptions:
- Path exists and nodes are in same tree component
- Current values fit in int64
Thread Safety: NOT safe for concurrent use.
func (*PathUpdateEngine) PathUpdate ¶
func (pue *PathUpdateEngine) PathUpdate(ctx context.Context, u, v string, delta int64, logger *slog.Logger) error
PathUpdate adds delta to all node values on path from u to v.
Description:
Decomposes path into heavy path segments and applies range update to each segment. Handles LCA correctly (DecomposePath already excludes from second path). Uses lazy propagation for efficiency. Invalidates query cache after update for correctness.
Algorithm:
Time: O(log² V) = O(log V) segments × O(log V) per range update Space: O(log V) - segment decomposition
Inputs:
- ctx: Context for cancellation. Must not be nil.
- u: Start node ID. Must exist in graph and not be empty.
- v: End node ID. Must exist in graph and not be empty.
- delta: Value to add to each node on path.
- logger: Structured logger for events. Must not be nil.
Outputs:
- error: Non-nil if nodes don't exist, cross-tree in forest, or update fails.
Special Cases:
- PathUpdate(u, u, delta): Updates single node
- PathUpdate(u, parent[u], delta): Updates two nodes
- delta == 0: No-op fast path, still recorded in CRS
Example:
// Add 5 to complexity along call path
err := engine.PathUpdate(ctx, "main", "leaf", 5, logger)
if err != nil {
return fmt.Errorf("path update: %w", err)
}
CRS Integration:
Records update as StepRecord with node IDs, delta, segments updated. Sub-steps recorded for each segment update.
Limitations:
- Only works within single tree component (error for cross-tree in forest mode)
- Requires SUM aggregation (MIN/MAX/GCD not supported for updates - GR-19b restriction)
- Query cache invalidated on EVERY update via Purge() - entire cache discarded (O(cache_size) cost) This impacts mixed read-heavy workloads: after ANY update, all cached queries must be recomputed For update-heavy workloads, consider disabling query cache to avoid purge overhead
- NOT safe for concurrent updates (caller must synchronize with external lock)
- Segment tree version incremented (may invalidate external references)
Assumptions:
- Segment tree values correspond to HLD position array
- Node values fit in int64
- Caller has validated graph hasn't changed since construction
Thread Safety: NOT safe for concurrent use. Caller must synchronize.
func (*PathUpdateEngine) Stats ¶
func (pue *PathUpdateEngine) Stats() PathUpdateStats
Stats returns update statistics.
Description:
Thread-safe stats retrieval using atomic operations. Returns snapshot of current statistics. Includes average segments per update.
Inputs:
- None
Outputs:
- PathUpdateStats: Snapshot of statistics including update count, latency, segments per update
Limitations:
- Snapshot may be stale immediately after return
- AvgSegmentsPerUpdate excludes no-op updates (counted as 0 segments)
Assumptions:
- Atomic loads provide consistent point-in-time snapshot
- UpdateCount > 0 when calculating averages
Thread Safety: Safe for concurrent use (atomic reads).
type PathUpdateStats ¶
type PathUpdateStats struct {
UpdateCount int64 // Total updates
TotalUpdateLatency time.Duration // Sum of all update durations
AvgUpdateLatency time.Duration // Average update duration
SegmentsUpdated int64 // Total segments updated across all operations
AvgSegmentsPerUpdate float64 // Average segments per update
}
PathUpdateStats contains update statistics.
Description:
Statistics collected across all updates on this engine.
Thread Safety: Snapshot at time of Stats() call, may be stale.
type ProgressFunc ¶
type ProgressFunc func(progress BuildProgress)
ProgressFunc is a callback function for build progress updates.
type ProgressPhase ¶
type ProgressPhase int
ProgressPhase indicates which phase of building is in progress.
const ( // ProgressPhaseCollecting indicates symbols are being collected as nodes. ProgressPhaseCollecting ProgressPhase = iota // ProgressPhaseExtractingEdges indicates edges are being extracted. ProgressPhaseExtractingEdges // ProgressPhaseLSPEnrichment indicates LSP enrichment is resolving placeholders. // GR-74: Inserted between edge extraction and finalization. ProgressPhaseLSPEnrichment // ProgressPhaseFinalizing indicates the graph is being finalized. ProgressPhaseFinalizing )
func (ProgressPhase) String ¶
func (p ProgressPhase) String() string
String returns the string representation of the ProgressPhase.
type QueryCacheStats ¶
type QueryCacheStats struct {
// CallersHits is the number of callers cache hits.
CallersHits int64 `json:"callers_hits"`
// CallersMisses is the number of callers cache misses.
CallersMisses int64 `json:"callers_misses"`
// CallersEvictions is the number of callers cache evictions.
CallersEvictions int64 `json:"callers_evictions"`
// CallersSize is the current number of entries in the callers cache.
CallersSize int `json:"callers_size"`
// CalleesHits is the number of callees cache hits.
CalleesHits int64 `json:"callees_hits"`
// CalleesMisses is the number of callees cache misses.
CalleesMisses int64 `json:"callees_misses"`
// CalleesEvictions is the number of callees cache evictions.
CalleesEvictions int64 `json:"callees_evictions"`
// CalleesSize is the current number of entries in the callees cache.
CalleesSize int `json:"callees_size"`
// PathsHits is the number of paths cache hits.
PathsHits int64 `json:"paths_hits"`
// PathsMisses is the number of paths cache misses.
PathsMisses int64 `json:"paths_misses"`
// PathsEvictions is the number of paths cache evictions.
PathsEvictions int64 `json:"paths_evictions"`
// PathsSize is the current number of entries in the paths cache.
PathsSize int `json:"paths_size"`
// TotalHits is the sum of all cache hits.
TotalHits int64 `json:"total_hits"`
// TotalMisses is the sum of all cache misses.
TotalMisses int64 `json:"total_misses"`
// TotalEvictions is the sum of all cache evictions.
TotalEvictions int64 `json:"total_evictions"`
// HitRate is the cache hit rate (0.0 to 1.0). Zero if no queries.
HitRate float64 `json:"hit_rate"`
}
QueryCacheStats contains statistics for query caches.
Description:
Provides visibility into query cache performance for monitoring and debugging. All counts are since cache creation or last Purge.
Thread Safety: Fields are atomic snapshots, safe for concurrent use.
type QueryOption ¶
type QueryOption func(*QueryOptions)
QueryOption is a functional option for configuring queries.
func WithLimit ¶
func WithLimit(n int) QueryOption
WithLimit sets the maximum number of results.
If n <= 0, uses default (1000). If n > 10000, clamps to 10000.
func WithMaxDepth ¶
func WithMaxDepth(d int) QueryOption
WithMaxDepth sets the maximum traversal depth.
If d < 0, uses default (10). If d > 100, clamps to 100.
func WithTimeout ¶
func WithTimeout(d time.Duration) QueryOption
WithTimeout sets the per-query timeout.
type QueryOptions ¶
type QueryOptions struct {
// Limit is the maximum number of results (default: 1000, max: 10000).
Limit int
// MaxDepth is the maximum traversal depth (default: 10, max: 100).
MaxDepth int
// Timeout is the per-query timeout (0 = use context deadline).
Timeout time.Duration
}
QueryOptions configures query behavior.
func DefaultQueryOptions ¶
func DefaultQueryOptions() QueryOptions
DefaultQueryOptions returns sensible defaults for queries.
type QueryResult ¶
type QueryResult struct {
// Symbols contains the matching symbols.
Symbols []*ast.Symbol
// Truncated is true if limit was reached or context was cancelled.
Truncated bool
// Duration is the query execution time.
Duration time.Duration
}
QueryResult wraps query results with metadata.
type RateLimiter ¶
type RateLimiter interface {
// Allow returns true if the query is allowed, false if rate limited.
Allow() bool
// Wait blocks until a query is allowed or context is canceled.
Wait(ctx context.Context) error
// Stats returns rate limiter statistics.
Stats() RateLimiterStats
}
RateLimiter controls query rate to prevent DoS.
Description:
Implements rate limiting using token bucket, leaky bucket, or similar algorithms. Protects against query flooding.
Thread Safety: All methods must be safe for concurrent use.
type RateLimiterStats ¶
type RateLimiterStats struct {
Allowed int64 // Total requests allowed
Rejected int64 // Total requests rejected
QPS float64 // Current queries per second
}
RateLimiterStats contains rate limiting metrics.
type ReducibilityResult ¶
type ReducibilityResult struct {
// IsReducible is true if the entire graph is reducible.
IsReducible bool
// Score is the percentage of nodes NOT in irreducible regions.
// Range: 0.0 to 1.0, where 1.0 means fully reducible.
// Rounded to 4 decimal places for consistency.
Score float64
// IrreducibleRegions contains detected irreducible subgraphs.
IrreducibleRegions []*IrreducibleRegion
// Summary contains aggregate statistics.
Summary ReducibilitySummary
// Recommendation provides actionable guidance.
Recommendation string
// DomTree is the dominator tree used for analysis.
DomTree *DominatorTree
// NodeCount is total nodes analyzed.
NodeCount int
// EdgeCount is total edges analyzed.
EdgeCount int
// CrossEdgeCount is the number of cross edges found.
CrossEdgeCount int
}
ReducibilityResult contains the reducibility analysis output.
A graph is reducible if all back edges go to dominators (natural loops only). Irreducible graphs have cross edges that create multi-entry regions.
Thread Safety: Safe for concurrent use after construction.
type ReducibilitySummary ¶
type ReducibilitySummary struct {
TotalNodes int
IrreducibleNodeCount int
IrreducibleRegionCount int
WellStructuredPercent float64
}
ReducibilitySummary contains aggregate statistics.
type RefreshConfig ¶
type RefreshConfig struct {
// MaxFilesToRefresh limits incremental refresh size.
// If exceeded, returns error suggesting full rebuild.
// Default: 50
MaxFilesToRefresh int
// ParallelParsing enables concurrent file parsing.
// Default: true
ParallelParsing bool
// ParallelWorkers is the number of parallel parse workers.
// Default: 4
ParallelWorkers int
// Timeout is the maximum time for a refresh operation.
// Default: 30s
Timeout time.Duration
}
RefreshConfig configures refresh behavior.
func DefaultRefreshConfig ¶
func DefaultRefreshConfig() RefreshConfig
DefaultRefreshConfig returns sensible defaults.
type RefreshResult ¶
type RefreshResult struct {
// FilesRefreshed is the number of files processed.
FilesRefreshed int
// NodesRemoved is the count of nodes removed from old files.
NodesRemoved int
// NodesAdded is the count of nodes added from new parses.
NodesAdded int
// Duration is how long the refresh took.
Duration time.Duration
// ParseErrors contains errors for files that failed to parse.
ParseErrors []FileParseError
}
RefreshResult contains statistics about an incremental refresh.
type Refresher ¶
type Refresher struct {
// contains filtered or unexported fields
}
Refresher handles incremental graph updates.
Description:
Provides incremental refresh capability for the code graph. When files are modified by agent tools, the Refresher can update only the affected portions of the graph without requiring a full rebuild.
Thread Safety:
The Refresher is safe for concurrent use. During refresh, a clone of the graph is modified, then atomically swapped.
func NewRefresher ¶
func NewRefresher(graph **Graph, parsers *ast.ParserRegistry, opts ...RefresherOption) *Refresher
NewRefresher creates a new Refresher.
Description:
Creates a refresher bound to a graph pointer. The graph pointer can be swapped atomically during refresh operations.
Inputs:
graph - Pointer to the graph pointer (allows atomic swap). parsers - Parser registry for re-parsing files. opts - Optional configuration.
Outputs:
*Refresher - The configured refresher.
func (*Refresher) GetGraph ¶
GetGraph returns the current graph.
Thread Safety:
Safe for concurrent use.
func (*Refresher) RefreshFiles ¶
RefreshFiles incrementally updates the graph for modified files.
Description:
Re-parses only the specified files and updates graph nodes. Uses a copy-on-write pattern: clones the graph, modifies the clone, then atomically swaps if successful.
Inputs:
ctx - Context for cancellation and timeout. paths - Files to refresh (absolute paths).
Outputs:
*RefreshResult - Statistics about the refresh. error - Non-nil if refresh failed completely.
Errors:
Returns error if: - Too many files (exceeds MaxFilesToRefresh) - Context cancelled or timed out - Graph is nil
Thread Safety:
Safe for concurrent use. Uses copy-on-write to avoid blocking readers during refresh.
type RefresherOption ¶
type RefresherOption func(*Refresher)
RefresherOption configures a Refresher.
func WithRefresherConfig ¶
func WithRefresherConfig(config RefreshConfig) RefresherOption
WithRefresherConfig sets the refresh configuration.
func WithRefresherLogger ¶
func WithRefresherLogger(logger *slog.Logger) RefresherOption
WithRefresherLogger sets the logger.
type SESEError ¶
type SESEError struct {
Message string
}
SESEError represents an error in SESE region detection.
type SESERegion ¶
type SESERegion struct {
// Entry is the single entry node ID.
Entry string
// Exit is the single exit node ID.
Exit string
// Nodes contains all node IDs in the region (including entry/exit).
Nodes []string
// Size is the node count (len(Nodes)).
Size int
// Parent is the enclosing region (nil if top-level).
Parent *SESERegion
// Children are nested regions within this region.
Children []*SESERegion
// Depth is the nesting depth (0 = top-level).
Depth int
}
SESERegion represents a single-entry single-exit region.
A SESE region has exactly one entry and one exit node where:
- All paths into the region go through entry
- All paths out of the region go through exit
- Entry dominates all nodes in the region
- Exit post-dominates all nodes in the region
Thread Safety: Safe for concurrent use after construction.
type SESEResult ¶
type SESEResult struct {
// Regions contains all SESE regions found.
Regions []*SESERegion
// TopLevel contains regions with no parent (depth 0).
TopLevel []*SESERegion
// RegionOf maps node ID → innermost containing region.
RegionOf map[string]*SESERegion
// MaxDepth is the maximum nesting depth found.
MaxDepth int
// Extractable contains regions suitable for extraction.
// Populated by FilterExtractable().
Extractable []*SESERegion
// NodeCount is the total nodes analyzed.
NodeCount int
// RegionCount is the total regions found.
RegionCount int
}
SESEResult contains all detected SESE regions.
Thread Safety: Safe for concurrent use after construction.
func (*SESEResult) FilterExtractable ¶
func (r *SESEResult) FilterExtractable(minSize, maxSize int) []*SESERegion
FilterExtractable returns regions suitable for extraction based on size.
Description:
Filters regions by node count. Regions that are too small may not be worth extracting, while regions that are too large should be split further.
Inputs:
- minSize: Minimum nodes in region (inclusive). Use 1 to include all.
- maxSize: Maximum nodes in region (inclusive). Use a large value for no limit.
Outputs:
- []*SESERegion: Regions within the size range. Never nil.
Example:
// Find regions with 3-50 nodes (good candidates for extraction) extractable := result.FilterExtractable(3, 50)
Thread Safety: Safe for concurrent use.
type SegmentTree ¶
type SegmentTree struct {
// contains filtered or unexported fields
}
SegmentTree provides efficient range queries and updates over an array.
Description:
A segment tree supporting O(log N) range queries and updates for associative aggregation functions (sum, min, max, gcd). Supports lazy propagation for efficient range updates (SUM only).
Invariants:
- Tree array has size 2*paddedSize where paddedSize is next power of 2
- tree[i] represents aggregate over range [L, R]
- Parent(i) = i/2, LeftChild(i) = 2*i, RightChild(i) = 2*i+1
- lazy[i] stores pending updates for node i's children (SUM only)
- After build, tree[1] = aggregate over entire array
- version increments on each Update/RangeUpdate
Thread Safety:
- All operations are protected by RWMutex
- Query operations acquire read lock (thread-safe for concurrent reads)
- Update operations acquire write lock (not safe for concurrent updates)
- Lazy propagation during Query requires write lock on affected nodes
func NewSegmentTree ¶
func NewSegmentTree(ctx context.Context, arr []int64, aggFunc AggregateFunc) (*SegmentTree, error)
NewSegmentTree creates a segment tree from an array of values.
Description:
Constructs a segment tree supporting range queries/updates over arr. Uses bottom-up construction for O(N) build time. Pads array to next power of 2 for proper tree structure.
Algorithm:
Time: O(N) where N = len(arr) Space: O(N) - tree array size 2*paddedSize
Inputs:
- ctx: Context for cancellation. Must not be nil.
- arr: Initial array values. Must not be empty.
- aggFunc: Aggregation function (SUM, MIN, MAX, GCD).
Outputs:
- *SegmentTree: Constructed segment tree. Never nil on success.
- error: Non-nil if arr is empty, too large, or aggFunc is invalid.
Optimizations:
- Single allocation for tree and lazy arrays
- Bottom-up construction (iterative, not recursive)
- Pre-computed power-of-2 padding
- Context cancellation checked periodically
CRS Integration:
- Records construction timing and metadata
- Deterministic output for caching
Example:
arr := []int64{3, 1, 4, 2, 5, 7, 6, 8}
tree, err := NewSegmentTree(ctx, arr, AggregateSUM)
if err != nil {
return fmt.Errorf("build segment tree: %w", err)
}
sum, _ := tree.Query(ctx, 2, 5) // Range [2,5] sum = 18
Thread Safety: Safe for concurrent use with different arrays.
func (*SegmentTree) CacheKey ¶
func (st *SegmentTree) CacheKey() string
CacheKey generates a cache key for this segment tree.
Description:
Returns a deterministic cache key based on tree structure and version. Includes version to handle updates properly.
Thread Safety: Safe for concurrent use (read-only, protected by RWMutex).
func (*SegmentTree) GetValue ¶
GetValue retrieves the current value at arr[index].
Description:
Returns the current value at index, accounting for lazy updates. Equivalent to Query(ctx, index, index) but more explicit.
Algorithm:
Time: O(log N) - must push down lazy updates Space: O(1)
Thread Safety: Safe for concurrent reads (protected by RWMutex).
func (*SegmentTree) MemoryUsage ¶
func (st *SegmentTree) MemoryUsage() int
MemoryUsage estimates memory usage in bytes.
func (*SegmentTree) Query ¶
Query computes the aggregate over range [left, right] (inclusive).
Description:
Queries the segment tree for the aggregate value over [left, right]. Automatically pushes down lazy updates before reading nodes.
Algorithm:
Time: O(log N) where N = size of array Space: O(1) - iterative implementation
Inputs:
- ctx: Context for tracing.
- left: Left boundary (0-indexed, inclusive). Must be in [0, size).
- right: Right boundary (0-indexed, inclusive). Must be in [left, size).
Outputs:
- int64: Aggregate value over [left, right].
- error: Non-nil if range is invalid.
Example:
sum, err := tree.Query(ctx, 2, 5) // Sum of arr[2..5]
Thread Safety: Safe for concurrent reads (protected by RWMutex).
func (*SegmentTree) RangeUpdate ¶
RangeUpdate adds delta to all elements in [left, right].
Description:
Efficiently updates a range using lazy propagation. Actual updates are deferred until the nodes are queried or updated again. CRITICAL: Only supports SUM aggregation (additive operation).
Algorithm:
Time: O(log N) Space: O(1)
Inputs:
- ctx: Context for tracing.
- left: Left boundary (0-indexed, inclusive). Must be in [0, size).
- right: Right boundary (0-indexed, inclusive). Must be in [left, size).
- delta: Value to add to each element in range.
Outputs:
- error: Non-nil if range is invalid or aggFunc is not SUM.
Example:
err := tree.RangeUpdate(ctx, 0, 3, 5) // Add 5 to arr[0..3]
Limitations:
- Only supports additive updates (delta added to existing values)
- Does not support set operations (use Update for single elements)
- Only works with AggregateSUM (MIN/MAX are not additive)
Thread Safety: NOT safe for concurrent use. Caller must synchronize.
func (*SegmentTree) Stats ¶
func (st *SegmentTree) Stats() SegmentTreeStats
Stats returns statistics about the segment tree.
Thread Safety: Safe for concurrent use (read-only, protected by RWMutex).
func (*SegmentTree) Update ¶
Update sets arr[index] = value and updates tree.
Description:
Updates a single element and propagates change up to root. Pushes down lazy updates on path from leaf to root.
Algorithm:
Time: O(log N) Space: O(1)
Inputs:
- ctx: Context for tracing.
- index: Array index to update (0-indexed). Must be in [0, size).
- value: New value for arr[index].
Outputs:
- error: Non-nil if index is out of bounds.
Example:
err := tree.Update(ctx, 3, 10) // Set arr[3] = 10
Thread Safety: NOT safe for concurrent use. Caller must synchronize.
func (*SegmentTree) Validate ¶
func (st *SegmentTree) Validate() error
Validate checks segment tree invariants.
Description:
Verifies: - Tree and lazy arrays have correct size - Root aggregate matches expected value - Parent-child relationships are correct - Lazy array consistency
Complexity: O(N) time
Thread Safety: Safe for concurrent use (read-only, protected by RWMutex).
type SegmentTreeStats ¶
type SegmentTreeStats struct {
Size int // Number of leaves (original array size)
PaddedSize int // Padded size (next power of 2)
TreeSize int // Total tree array size
Height int // Tree height (log2 of padded size)
AggFunc AggregateFunc // Aggregation function
BuildTime time.Duration // Construction time
QueryCount int64 // Queries performed
UpdateCount int64 // Updates performed
Version int64 // Current version
MemoryBytes int // Approximate memory usage
}
SegmentTreeStats contains statistics about the segment tree.
type SerializableEdge ¶
type SerializableEdge struct {
// FromID is the ID of the source node.
FromID string `json:"from_id"`
// ToID is the ID of the target node.
ToID string `json:"to_id"`
// Type is the human-readable edge type string (e.g., "calls", "imports").
Type string `json:"type"`
// TypeCode is the integer edge type for exact reconstruction.
TypeCode EdgeType `json:"type_code"`
// Location is where the relationship is expressed in code.
Location ast.Location `json:"location"`
}
SerializableEdge is the JSON-serializable representation of an Edge.
type SerializableGraph ¶
type SerializableGraph struct {
// SchemaVersion identifies the serialization format version.
SchemaVersion string `json:"schema_version"`
// ProjectRoot is the absolute path to the project root directory.
ProjectRoot string `json:"project_root"`
// BuiltAtMilli is the Unix timestamp in milliseconds when the graph was frozen.
BuiltAtMilli int64 `json:"built_at_milli"`
// GraphHash is the deterministic hash of the graph structure.
GraphHash string `json:"graph_hash"`
// Nodes contains all nodes in the graph, sorted by ID.
Nodes []SerializableNode `json:"nodes"`
// Edges contains all edges in the graph.
Edges []SerializableEdge `json:"edges"`
// FileMtimes records file modification times (Unix seconds) at build time.
// CRS-19: Used for staleness detection across sessions.
FileMtimes map[string]int64 `json:"file_mtimes,omitempty"`
}
SerializableGraph is the JSON-serializable representation of a Graph.
Description:
Contains all data needed to reconstruct a Graph from JSON. Nodes are sorted by ID for deterministic output, enabling reliable diffing and content hashing.
Thread Safety: SerializableGraph is a value type with no internal state.
type SerializableNode ¶
type SerializableNode struct {
// ID is the unique node identifier (same as Symbol.ID).
ID string `json:"id"`
// Symbol is the underlying AST symbol. ast.Symbol already has JSON tags.
Symbol *ast.Symbol `json:"symbol"`
}
SerializableNode is the JSON-serializable representation of a Node.
type SnapshotDiff ¶
type SnapshotDiff struct {
// BaseSnapshotID is the ID of the base snapshot.
BaseSnapshotID string `json:"base_snapshot_id"`
// TargetSnapshotID is the ID of the target snapshot.
TargetSnapshotID string `json:"target_snapshot_id"`
// NodesAdded are node IDs present in target but not in base.
NodesAdded []string `json:"nodes_added"`
// NodesRemoved are node IDs present in base but not in target.
NodesRemoved []string `json:"nodes_removed"`
// NodesModified are nodes that changed between snapshots.
NodesModified []NodeDiff `json:"nodes_modified"`
// EdgesAdded is the count of edges in target but not in base.
EdgesAdded int `json:"edges_added"`
// EdgesRemoved is the count of edges in base but not in target.
EdgesRemoved int `json:"edges_removed"`
// Summary contains aggregate statistics about the diff.
Summary DiffSummary `json:"summary"`
}
SnapshotDiff contains the differences between two graph snapshots.
func DiffSnapshots ¶
func DiffSnapshots(base, target *Graph, baseSnapshotID, targetSnapshotID string) (*SnapshotDiff, error)
DiffSnapshots computes the differences between two graphs.
Description:
Compares two graphs (typically loaded from snapshots) and produces a SnapshotDiff describing what changed. Comparison is by node ID — symbols with the same ID in both graphs are compared for modifications. Moved symbols (same name, different file) show as remove + add.
Inputs:
base - The base graph for comparison. Must not be nil. target - The target graph for comparison. Must not be nil. baseSnapshotID - ID of the base snapshot (for labeling). targetSnapshotID - ID of the target snapshot (for labeling).
Outputs:
*SnapshotDiff - The computed differences. error - Non-nil if either graph is nil.
Complexity:
O(V + E) where V is max(baseNodes, targetNodes) and E is max(baseEdges, targetEdges).
Thread Safety:
Safe for concurrent use on frozen graphs.
type SnapshotManager ¶
type SnapshotManager struct {
// contains filtered or unexported fields
}
SnapshotManager manages saving and loading graph snapshots in BadgerDB.
Description:
Provides CRUD operations for graph snapshots stored as gzip-compressed JSON in BadgerDB. Each snapshot stores the full SerializableGraph plus metadata for listing and filtering.
Thread Safety:
Safe for concurrent use. BadgerDB handles its own concurrency control.
func NewSnapshotManager ¶
NewSnapshotManager creates a new SnapshotManager.
Description:
Creates a manager that uses the given BadgerDB instance for persistence. The DB should be opened by the caller and closed when no longer needed.
Inputs:
db - An opened BadgerDB instance. Must not be nil. logger - Logger for diagnostic output. Must not be nil.
Outputs:
*SnapshotManager - The configured manager. error - Non-nil if db or logger is nil.
func (*SnapshotManager) Delete ¶
func (m *SnapshotManager) Delete(ctx context.Context, snapshotID string) error
Delete removes a snapshot from BadgerDB.
Description:
Removes all keys associated with the given snapshot ID: data, metadata, and the reverse index entry. If the deleted snapshot was the "latest", the latest pointer is also removed.
Inputs:
ctx - Context for cancellation. Must not be nil. snapshotID - The snapshot ID to delete. Must not be empty.
Outputs:
error - Non-nil if the snapshot is not found or deletion fails.
func (*SnapshotManager) List ¶
func (m *SnapshotManager) List(ctx context.Context, projectHash string, limit int) ([]*SnapshotMetadata, error)
List returns metadata for snapshots matching the optional project hash filter.
Description:
Iterates BadgerDB keys with the snapshot prefix to find metadata entries. If projectHash is non-empty, only snapshots for that project are returned. Results are ordered by CreatedAtMilli descending (newest first).
Inputs:
ctx - Context for cancellation. Must not be nil. projectHash - Optional filter. If empty, returns all snapshots. limit - Maximum number of results. If <= 0, defaults to 100.
Outputs:
[]*SnapshotMetadata - The matching snapshots. error - Non-nil if the read fails.
func (*SnapshotManager) Load ¶
func (m *SnapshotManager) Load(ctx context.Context, snapshotID string) (*Graph, *SnapshotMetadata, error)
Load retrieves a graph snapshot by its ID.
Description:
Loads the compressed data from BadgerDB, decompresses it, and reconstructs the graph using FromSerializable.
Inputs:
ctx - Context for cancellation. Must not be nil. snapshotID - The snapshot ID to load. Must not be empty.
Outputs:
*Graph - The reconstructed graph in read-only state. *SnapshotMetadata - The snapshot metadata. error - Non-nil if the snapshot is not found or reconstruction fails.
func (*SnapshotManager) LoadLatest ¶
func (m *SnapshotManager) LoadLatest(ctx context.Context, projectHash string) (*Graph, *SnapshotMetadata, error)
LoadLatest loads the most recent snapshot for a project.
Description:
Uses the "latest" pointer to find and load the most recent snapshot for the given project hash.
Inputs:
ctx - Context for cancellation. Must not be nil. projectHash - The SHA256(ProjectRoot)[:16] hash. Must not be empty.
Outputs:
*Graph - The reconstructed graph in read-only state. *SnapshotMetadata - The snapshot metadata. error - Non-nil if no snapshot exists or reconstruction fails.
func (*SnapshotManager) Save ¶
func (m *SnapshotManager) Save(ctx context.Context, g *Graph, label string) (*SnapshotMetadata, error)
Save persists a graph snapshot to BadgerDB.
Description:
Serializes the graph to JSON, gzip-compresses it, and stores it in BadgerDB along with metadata. Updates the "latest" pointer for the project.
Inputs:
ctx - Context for cancellation. Must not be nil. g - The graph to snapshot. Must not be nil and should be frozen. label - Optional human-readable label for the snapshot.
Outputs:
*SnapshotMetadata - Metadata about the saved snapshot. error - Non-nil if serialization or storage fails.
Key Schema:
graph:snap:{projectHash}:{snapshotID}:data → gzip(JSON(SerializableGraph))
graph:snap:{projectHash}:{snapshotID}:meta → JSON(SnapshotMetadata)
graph:snap:{projectHash}:latest → snapshotID
graph:snap:index:{snapshotID} → projectHash
type SnapshotMetadata ¶
type SnapshotMetadata struct {
// SnapshotID is the unique identifier for this snapshot.
// Derived from SHA256(ProjectRoot + BuiltAtMilli)[:16].
SnapshotID string `json:"snapshot_id"`
// ProjectRoot is the absolute path to the project root.
ProjectRoot string `json:"project_root"`
// ProjectHash is SHA256(ProjectRoot)[:16] for key grouping.
ProjectHash string `json:"project_hash"`
// GraphHash is the deterministic hash of the graph structure.
GraphHash string `json:"graph_hash"`
// Label is an optional human-readable label.
Label string `json:"label,omitempty"`
// CreatedAtMilli is when the snapshot was saved (Unix milliseconds UTC).
CreatedAtMilli int64 `json:"created_at_milli"`
// NodeCount is the number of nodes in the graph.
NodeCount int `json:"node_count"`
// EdgeCount is the number of edges in the graph.
EdgeCount int `json:"edge_count"`
// SchemaVersion is the serialization schema version.
SchemaVersion string `json:"schema_version"`
// CompressedSize is the size of the gzip-compressed JSON payload in bytes.
CompressedSize int64 `json:"compressed_size"`
// ContentHash is the SHA256 hash of the compressed payload.
ContentHash string `json:"content_hash"`
}
SnapshotMetadata contains metadata about a saved graph snapshot.
type StalenessChecker ¶
type StalenessChecker struct {
// contains filtered or unexported fields
}
StalenessChecker detects whether the code graph is stale relative to the current working tree.
Description:
Records file modification times at build time and spot-checks a sample of files at query time. Results are cached for StalenessCheckCacheTTL (60s) to avoid repeated stat syscalls.
Thread Safety: Safe for concurrent use. Uses sync.RWMutex for cache.
func NewStalenessChecker ¶
func NewStalenessChecker(projectRoot string, fileMtimes map[string]int64) *StalenessChecker
NewStalenessChecker creates a staleness checker from a graph's recorded file mtimes.
Inputs:
- projectRoot: Absolute path to the project root.
- fileMtimes: Map of relative file path → mtime (Unix seconds) recorded at build time.
Outputs:
- *StalenessChecker: The checker, or nil if fileMtimes is nil/empty.
Thread Safety: Safe for concurrent use after construction.
func (*StalenessChecker) Check ¶
func (sc *StalenessChecker) Check() StalenessResult
Check performs a staleness check, returning a cached result if available.
Description:
If a cached result exists and is younger than StalenessCheckCacheTTL, returns it immediately. Otherwise, spot-checks up to StalenessCheckSampleSize files from the build-time mtime map against their current mtimes.
Outputs:
- StalenessResult: The check outcome.
Thread Safety: Safe for concurrent use.
func (*StalenessChecker) InvalidateCache ¶
func (sc *StalenessChecker) InvalidateCache()
InvalidateCache forces the next Check() to recompute staleness.
Thread Safety: Safe for concurrent use.
type StalenessResult ¶
type StalenessResult struct {
// IsStale is true if any files have changed since graph build.
IsStale bool
// ChangedFileCount is the number of files detected as changed.
ChangedFileCount int
// TotalFileCount is the total number of files checked.
TotalFileCount int
// PercentChanged is the ratio of changed files to total files (0.0-1.0).
PercentChanged float64
// CheckedAt is when this result was computed.
CheckedAt time.Time
// SampleBased is true if only a subset of files was checked.
SampleBased bool
}
StalenessResult contains the outcome of a staleness check.
Thread Safety: Immutable after construction.
type SubtreeQueryEngine ¶
type SubtreeQueryEngine struct {
// contains filtered or unexported fields
}
SubtreeQueryEngine provides efficient subtree queries using HLD position ranges.
Description:
Leverages HLD's DFS ordering property: subtree(v) occupies contiguous range [pos[v], pos[v]+size[v]) in position array. Enables O(log V) subtree queries with a single segment tree range query.
Invariants:
- pos[v] + size[v] <= nodeCount for all v
- All descendants of v have positions in [pos[v], pos[v]+size[v])
- Subtree ranges are non-overlapping (except for ancestors)
- Exactly one of hld or forest is non-nil
- In forest mode, subtrees are always within single tree (by definition)
Thread Safety:
Safe for concurrent queries (read-only operations with internal locking for stats).
func NewSubtreeQueryEngine ¶
func NewSubtreeQueryEngine(hld *HLDecomposition, segTree *SegmentTree, aggFunc AggregateFunc, crsRecorder crs.CRS, sessionID string) (*SubtreeQueryEngine, error)
NewSubtreeQueryEngine creates a subtree query engine for single-tree mode.
Description:
Combines HLD and segment tree for efficient subtree aggregation queries. Uses HLD's subtree size information to compute position ranges.
Algorithm:
Time: O(1) - store references and validate Space: O(1) - no allocations
Inputs:
- hld: Heavy-Light Decomposition. Must not be nil.
- segTree: Segment tree over node values. Must not be nil.
- aggFunc: Aggregation function. Must match segTree's aggregation.
- crsRecorder: CRS recorder for tracing (can be nil).
- sessionID: Session ID for CRS context (ignored if crsRecorder is nil).
Outputs:
- *SubtreeQueryEngine: Ready for queries. Never nil on success.
- error: Non-nil if inputs are invalid.
Example:
// Build HLD and segment tree hld, _ := BuildHLD(ctx, graph, "main") values := buildValueArray(hld) segTree, _ := NewSegmentTree(ctx, values, AggregateSUM) // Create subtree query engine engine, _ := NewSubtreeQueryEngine(hld, segTree, AggregateSUM, nil, "")
Limitations:
- Single tree only. For forests, use NewSubtreeQueryEngineForest.
- Aggregation function cannot be changed after construction.
Assumptions:
- HLD and segment tree are already built and valid.
- Segment tree values correspond to HLD positions.
Thread Safety: Safe for concurrent use.
func NewSubtreeQueryEngineForest ¶
func NewSubtreeQueryEngineForest(forest *HLDForest, segTree *SegmentTree, aggFunc AggregateFunc, crsRecorder crs.CRS, sessionID string) (*SubtreeQueryEngine, error)
NewSubtreeQueryEngineForest creates a subtree query engine for forest mode.
Description:
Like NewSubtreeQueryEngine but supports multiple disconnected trees. Automatically routes queries to the correct tree component.
Algorithm:
Time: O(1) - store references and validate Space: O(1) - no allocations
Inputs:
- forest: HLD forest. Must not be nil.
- segTree: Segment tree over ALL forest nodes. Must not be nil.
- aggFunc: Aggregation function. Must match segTree's aggregation.
- crsRecorder: CRS recorder for tracing (can be nil).
- sessionID: Session ID for CRS context (ignored if crsRecorder is nil).
Outputs:
- *SubtreeQueryEngine: Ready for queries. Never nil on success.
- error: Non-nil if inputs are invalid.
Example:
forest, _ := BuildHLDForest(ctx, graph, true) values := buildForestValueArray(forest) segTree, _ := NewSegmentTree(ctx, values, AggregateSUM) engine, _ := NewSubtreeQueryEngineForest(forest, segTree, AggregateSUM, nil, "")
Limitations:
- Segment tree must span entire forest (size = forest.TotalNodes()).
Assumptions:
- Forest and segment tree are already built and valid.
- Segment tree values correspond to forest global positions.
Thread Safety: Safe for concurrent use.
func (*SubtreeQueryEngine) ClearCache ¶
func (sqe *SubtreeQueryEngine) ClearCache()
ClearCache clears the subtree range cache.
Description:
Clears all cached subtree ranges. Useful for long-lived engines that have queried many unique nodes and want to free memory.
Algorithm:
Time: O(n) where n is number of cached entries Space: O(1)
Limitations:
Cache entries are only 16 bytes each, so this is rarely needed. Clearing the cache will cause next queries to recompute ranges.
Thread Safety: Safe for concurrent use.
func (*SubtreeQueryEngine) Stats ¶
func (sqe *SubtreeQueryEngine) Stats() SubtreeQueryStats
Stats returns query statistics.
Description:
Provides query count, latency metrics, and average subtree size. Thread-safe via mutex locking.
Algorithm:
Time: O(1) Space: O(1)
Outputs:
SubtreeQueryStats with current statistics
Thread Safety: Safe for concurrent use.
func (*SubtreeQueryEngine) SubtreeGCD ¶
SubtreeGCD computes GCD of all values in subtree.
Description:
Convenience wrapper for SubtreeQuery with GCD aggregation. Requires engine to be configured with AggregateGCD.
Algorithm:
Time: O(log V) - delegates to SubtreeQuery Space: O(1)
Inputs:
- ctx: Context for cancellation. Must not be nil.
- v: Root node of subtree. Must exist in graph.
Outputs:
- int64: GCD of all values in subtree(v).
- error: Non-nil if node doesn't exist or query fails.
Thread Safety: Safe for concurrent use.
func (*SubtreeQueryEngine) SubtreeMax ¶
SubtreeMax computes maximum value in subtree.
Description:
Convenience wrapper for SubtreeQuery with MAX aggregation. Requires engine to be configured with AggregateMAX.
Algorithm:
Time: O(log V) - delegates to SubtreeQuery Space: O(1)
Inputs:
- ctx: Context for cancellation. Must not be nil.
- v: Root node of subtree. Must exist in graph.
Outputs:
- int64: Maximum value in subtree(v).
- error: Non-nil if node doesn't exist or query fails.
Example:
maxLatency, err := engine.SubtreeMax(ctx, "handler")
Thread Safety: Safe for concurrent use.
func (*SubtreeQueryEngine) SubtreeMin ¶
SubtreeMin computes minimum value in subtree.
Description:
Convenience wrapper for SubtreeQuery with MIN aggregation. Requires engine to be configured with AggregateMIN.
Algorithm:
Time: O(log V) - delegates to SubtreeQuery Space: O(1)
Inputs:
- ctx: Context for cancellation. Must not be nil.
- v: Root node of subtree. Must exist in graph.
Outputs:
- int64: Minimum value in subtree(v).
- error: Non-nil if node doesn't exist or query fails.
Example:
minMemory, err := engine.SubtreeMin(ctx, "allocator")
Thread Safety: Safe for concurrent use.
func (*SubtreeQueryEngine) SubtreeNodes ¶
SubtreeNodes returns all node IDs in subtree(v).
Description:
Materializes the subtree as a list of node IDs. Useful for debugging but slower than aggregation queries.
Algorithm:
Time: O(size[v]) - collect all nodes in subtree Space: O(size[v]) - store node IDs
Inputs:
- ctx: Context for cancellation. Must not be nil.
- v: Root node of subtree. Must exist in graph.
Outputs:
- []string: Node IDs in DFS order (same as position order)
- error: Non-nil if node doesn't exist
Example:
nodes, err := engine.SubtreeNodes(ctx, "package")
fmt.Printf("Package contains %d functions\n", len(nodes))
Limitations:
Slower than SubtreeQuery - use only when you need the actual node list.
Thread Safety: Safe for concurrent use (allocates new slice).
func (*SubtreeQueryEngine) SubtreeQuery ¶
SubtreeQuery computes aggregate over all nodes in subtree of v.
Description:
Uses HLD's contiguous subtree property to answer query with a single segment tree range query. Subtree(v) = [pos[v], pos[v]+size[v]).
Algorithm:
Time: O(log V) - single segment tree query Space: O(1) - no allocations
Inputs:
- ctx: Context for cancellation. Must not be nil.
- v: Root node of subtree. Must exist in graph.
Outputs:
- int64: Aggregate value over all nodes in subtree(v).
- error: Non-nil if node doesn't exist or query fails.
Special Cases:
- SubtreeQuery(leaf) = value[leaf] (single node, uses fast path)
- SubtreeQuery(root) = aggregate over entire tree
Example:
// Total complexity in package and all subpackages
totalComplexity, err := engine.SubtreeQuery(ctx, "pkg/parser")
if err != nil {
return fmt.Errorf("subtree query: %w", err)
}
fmt.Printf("Total complexity: %d\n", totalComplexity)
CRS Integration:
Records query as TraceStep with node ID, subtree size, result. Only records if CRS recorder is non-nil (H-PRE-6).
Limitations:
- Requires valid HLD and segment tree.
- Forest mode requires GetTreeOffset method.
Assumptions:
- Node v exists in the graph.
- HLD positions are valid and consistent.
Thread Safety: Safe for concurrent use.
func (*SubtreeQueryEngine) SubtreeRange ¶
func (sqe *SubtreeQueryEngine) SubtreeRange(ctx context.Context, v string) (start, end int, err error)
SubtreeRange returns the position range occupied by subtree(v).
Description:
Returns [start, end] (inclusive) position range for subtree. Useful for debugging and visualization.
Algorithm:
Time: O(1) - cached array lookups Space: O(1)
Inputs:
- ctx: Context for cancellation. Must not be nil.
- v: Root node of subtree. Must exist in graph.
Outputs:
- start: First position in subtree (pos[v])
- end: Last position in subtree (pos[v] + size[v] - 1)
- error: Non-nil if node doesn't exist
Example:
start, end, _ := engine.SubtreeRange(ctx, "parser")
fmt.Printf("Subtree occupies positions [%d, %d]\n", start, end)
Thread Safety: Safe for concurrent use.
func (*SubtreeQueryEngine) SubtreeSum ¶
SubtreeSum computes sum of all values in subtree.
Description:
Convenience wrapper for SubtreeQuery with SUM aggregation. Requires engine to be configured with AggregateSUM.
Algorithm:
Time: O(log V) - delegates to SubtreeQuery Space: O(1)
Inputs:
- ctx: Context for cancellation. Must not be nil.
- v: Root node of subtree. Must exist in graph.
Outputs:
- int64: Sum of all values in subtree(v).
- error: Non-nil if node doesn't exist or query fails.
Example:
totalLOC, err := engine.SubtreeSum(ctx, "main")
Thread Safety: Safe for concurrent use.
func (*SubtreeQueryEngine) Validate ¶
func (sqe *SubtreeQueryEngine) Validate() error
Validate checks that HLD and SegmentTree are compatible.
Description:
Verifies: - Exactly one of hld/forest is non-nil (L-PRE-2) - HLD/Forest and SegmentTree have same node count - All subtree ranges are valid (pos[v] + size[v] <= nodeCount)
Algorithm:
Time: O(V) - must check all nodes Space: O(1)
Outputs:
- error: Non-nil if validation fails
Thread Safety: Safe for concurrent use (read-only).
type SubtreeQueryStats ¶
type SubtreeQueryStats struct {
QueryCount int64
TotalLatency time.Duration
AvgLatency time.Duration
AvgSubtreeSize float64
}
SubtreeQueryStats provides query statistics.
type SubtreeUpdateEngine ¶
type SubtreeUpdateEngine struct {
*SubtreeQueryEngine // Embed query engine
// contains filtered or unexported fields
}
SubtreeUpdateEngine extends SubtreeQueryEngine with update operations.
Description:
Provides O(log V) subtree updates using single range update. All nodes in subtree(v) updated with one segment tree RangeUpdate.
Thread Safety:
NOT safe for concurrent updates. Caller must synchronize writes. Safe for concurrent reads (queries).
func NewSubtreeUpdateEngine ¶
func NewSubtreeUpdateEngine(sqe *SubtreeQueryEngine) (*SubtreeUpdateEngine, error)
NewSubtreeUpdateEngine creates a subtree update engine.
Description:
Wraps a SubtreeQueryEngine to add update operations. The query engine must already be constructed and valid.
Algorithm:
Time: O(1) - wrap reference Space: O(1)
Inputs:
- sqe: Subtree query engine. Must not be nil.
Outputs:
- *SubtreeUpdateEngine: Ready for updates. Never nil on success.
- error: Non-nil if query engine is nil.
Example:
queryEngine, _ := NewSubtreeQueryEngine(hld, segTree, AggregateSUM, nil, "") updateEngine, _ := NewSubtreeUpdateEngine(queryEngine)
Thread Safety: Safe for concurrent use (wraps read-only engine).
func (*SubtreeUpdateEngine) SubtreeDecrement ¶
func (sue *SubtreeUpdateEngine) SubtreeDecrement(ctx context.Context, v string) error
SubtreeDecrement decrements all values in subtree (delta=-1).
Description:
Convenience wrapper for SubtreeUpdate with delta=-1.
Thread Safety: NOT safe for concurrent updates.
func (*SubtreeUpdateEngine) SubtreeIncrement ¶
func (sue *SubtreeUpdateEngine) SubtreeIncrement(ctx context.Context, v string) error
SubtreeIncrement increments all values in subtree (delta=+1).
Description:
Convenience wrapper for SubtreeUpdate with delta=1.
Thread Safety: NOT safe for concurrent updates.
func (*SubtreeUpdateEngine) SubtreeSet ¶
SubtreeSet sets all node values in subtree(v) to a specific value.
Description:
Sets each node in subtree to value. Implementation iterates through positions since different nodes have different current values (cannot use single RangeUpdate).
Algorithm:
Time: O(size[v]) - must update each node individually Space: O(1)
Inputs:
- ctx: Context for cancellation. Must not be nil.
- v: Root node of subtree. Must exist in graph.
- value: Value to set for all nodes in subtree.
Outputs:
- error: Non-nil if node doesn't exist or update fails.
Note:
This is slower than SubtreeUpdate because nodes have different current values, so we cannot compute a single delta for all.
Thread Safety: NOT safe for concurrent updates.
func (*SubtreeUpdateEngine) SubtreeUpdate ¶
SubtreeUpdate adds delta to all node values in subtree(v).
Description:
Uses contiguous subtree range to perform single range update. Much more efficient than updating each node individually.
Algorithm:
Time: O(log V) - single range update with lazy propagation Space: O(1) - no allocations
Inputs:
- ctx: Context for cancellation. Must not be nil.
- v: Root node of subtree. Must exist in graph.
- delta: Value to add to each node in subtree.
Outputs:
- error: Non-nil if node doesn't exist or update fails.
Example:
// Increment complexity for entire package by 10
err := engine.SubtreeUpdate(ctx, "pkg/parser", 10)
if err != nil {
return fmt.Errorf("subtree update: %w", err)
}
CRS Integration:
Records update as TraceStep with node ID, delta, subtree size.
Thread Safety: NOT safe for concurrent updates.
func (*SubtreeUpdateEngine) UpdateStats ¶
func (sue *SubtreeUpdateEngine) UpdateStats() SubtreeUpdateStats
UpdateStats returns update statistics.
Description:
Provides update count, latency metrics, and total nodes updated.
Algorithm:
Time: O(1) Space: O(1)
Outputs:
SubtreeUpdateStats with current statistics
Thread Safety: Safe for concurrent use.
type SubtreeUpdateStats ¶
type SubtreeUpdateStats struct {
UpdateCount int64
TotalUpdateLatency time.Duration
AvgUpdateLatency time.Duration
NodesUpdated int64
}
SubtreeUpdateStats provides update statistics.
type TraceConfig ¶
type TraceConfig struct {
// ExcludeFromAnalysis lists file path prefixes to force-classify as non-production.
// Example: ["vendor/", "third_party/", "generated/"]
ExcludeFromAnalysis []string `yaml:"exclude_from_analysis"`
// IncludeOverride lists file path prefixes to force-classify as production,
// overriding the graph topology result.
// Example: ["integration/core/"]
IncludeOverride []string `yaml:"include_override"`
}
TraceConfig holds user-provided overrides for file classification.
Description:
Loaded from <projectRoot>/trace.config.yaml. All fields are optional. A missing config file is not an error (zero-config works out of the box).
Thread Safety: Safe for concurrent reads after construction.
type TraversalResult ¶
type TraversalResult struct {
// StartNode is the ID of the node where traversal began.
StartNode string
// VisitedNodes contains IDs of all visited nodes in traversal order.
VisitedNodes []string
// Edges contains all edges that were traversed.
Edges []*Edge
// Depth is the maximum depth reached during traversal.
Depth int
// Truncated indicates the traversal was stopped early due to
// limit, depth, or context cancellation.
Truncated bool
}
TraversalResult contains the results of a graph traversal query.
Source Files
¶
- analytics.go
- bbolt_codec.go
- build_result.go
- builder.go
- community.go
- crs_adapter.go
- dirty_tracker.go
- disk_graph.go
- dominators_articulation.go
- dominators_control.go
- dominators_frontier.go
- dominators_loops.go
- dominators_reducibility.go
- dominators_sese.go
- dominators_trees.go
- errors.go
- external.go
- file_classification.go
- file_watcher.go
- hierarchical.go
- hld.go
- hld_crs.go
- hld_extensions.go
- hld_iterative.go
- hld_path_queries.go
- hld_path_updates.go
- hld_queries.go
- hld_subtree_metrics.go
- hld_subtree_queries.go
- incremental.go
- lru.go
- lsp_enrichment.go
- materialize.go
- metrics.go
- pagerank.go
- parallel.go
- query.go
- refresher.go
- segment_tree.go
- serialization.go
- snapshot.go
- snapshot_diff.go
- staleness.go
- trace_config.go
- types.go