Skip to content

Commit 292ddfa

Browse files
docs(analysis): annotate flow and impact helpers
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
1 parent 1b3b242 commit 292ddfa

4 files changed

Lines changed: 46 additions & 0 deletions

File tree

internal/analysis/flows/builder.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ func (b *Builder) Rebuild(ctx context.Context, cfg Config) ([]Stats, error) {
7979
return result, err
8080
}
8181

82+
// deleteFlows removes all flows and memberships within the namespace.
83+
// @intent clear stale flow records before a fresh rebuild
84+
// @sideEffect deletes rows from flow_memberships and flows tables
8285
func deleteFlows(tx *gorm.DB, ns string) error {
8386
var ids []uint
8487
if err := tx.Model(&model.Flow{}).Where("namespace = ?", ns).Pluck("id", &ids).Error; err != nil {
@@ -93,6 +96,8 @@ func deleteFlows(tx *gorm.DB, ns string) error {
9396
return tx.Where("id IN ?", ids).Delete(&model.Flow{}).Error
9497
}
9598

99+
// findEntrypoints returns function and test nodes with no inbound calls edges.
100+
// @intent locate likely flow entry points by selecting nodes nothing else calls
96101
func findEntrypoints(tx *gorm.DB, ns string) ([]model.Node, error) {
97102
var nodes []model.Node
98103
inboundCalls := tx.Model(&model.Edge{}).

internal/analysis/flows/flows.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// @index Flow tracing engine that walks call edges into reusable flow records.
12
package flows
23

34
import (
@@ -21,10 +22,14 @@ type Tracer struct {
2122
store EdgeReader
2223
}
2324

25+
// TraceOptions bounds how far a single flow trace is allowed to expand.
26+
// @intent let callers cap traversal cost when tracing large call graphs
2427
type TraceOptions struct {
2528
MaxNodes int
2629
}
2730

31+
// TraceResult wraps a built flow with the metadata needed to detect truncation.
32+
// @intent communicate truncation status alongside the produced flow
2833
type TraceResult struct {
2934
Flow *model.Flow
3035
Truncated bool
@@ -52,6 +57,13 @@ func (t *Tracer) TraceFlow(ctx context.Context, startNodeID uint) (*model.Flow,
5257
return result.Flow, nil
5358
}
5459

60+
// TraceFlowBounded performs the BFS traversal that backs TraceFlow with explicit limits.
61+
// @intent expose a flow trace variant that can stop early when MaxNodes is reached
62+
// @param startNodeID graph node where traversal begins
63+
// @param opts traversal limits applied during BFS
64+
// @return TraceResult with the built flow and truncation metadata
65+
// @domainRule only calls edges enqueue new BFS nodes
66+
// @ensures Truncated is true only when MaxNodes stopped traversal
5567
func (t *Tracer) TraceFlowBounded(ctx context.Context, startNodeID uint, opts TraceOptions) (*TraceResult, error) {
5668
visited := map[uint]bool{}
5769
var members []model.FlowMembership

internal/analysis/impact/impact.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,15 @@ type Analyzer struct {
2424
store EdgeReader
2525
}
2626

27+
// RadiusOptions caps how far ImpactRadiusBounded is allowed to expand.
28+
// @intent let callers limit BFS depth and visited node count for safety
2729
type RadiusOptions struct {
2830
MaxDepth int
2931
MaxNodes int
3032
}
3133

34+
// RadiusResult reports the resolved nodes and whether limits truncated traversal.
35+
// @intent surface the visited node set together with truncation metadata
3236
type RadiusResult struct {
3337
Nodes []model.Node
3438
Truncated bool
@@ -60,6 +64,14 @@ func (a *Analyzer) ImpactRadius(ctx context.Context, nodeID uint, depth int) ([]
6064
return result.Nodes, nil
6165
}
6266

67+
// ImpactRadiusBounded performs the bidirectional BFS that backs ImpactRadius with explicit caps.
68+
// @intent expose a limit-aware blast radius traversal for cost-sensitive callers
69+
// @param nodeID starting node for the BFS
70+
// @param depth maximum BFS hop count (further capped by opts.MaxDepth)
71+
// @param opts traversal limits applied during BFS
72+
// @return RadiusResult with visited nodes and truncation metadata
73+
// @domainRule traverses outgoing and incoming edges in lock step at each depth
74+
// @ensures Truncated is true when MaxNodes stopped traversal or post-trim
6375
func (a *Analyzer) ImpactRadiusBounded(ctx context.Context, nodeID uint, depth int, opts RadiusOptions) (*RadiusResult, error) {
6476
if opts.MaxDepth > 0 && depth > opts.MaxDepth {
6577
depth = opts.MaxDepth

internal/analysis/incremental/incremental.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ type AnnotatingParser interface {
3636
Language() string
3737
}
3838

39+
// annotationWriter is the optional store capability needed to persist comment-derived annotations.
40+
// @intent allow incremental sync to skip annotation writes when the underlying store does not support them.
3941
type annotationWriter interface {
4042
UpsertAnnotation(ctx context.Context, ann *model.Annotation) error
4143
}
@@ -66,6 +68,9 @@ type Syncer struct {
6668
logger *slog.Logger
6769
}
6870

71+
// releaseContent drops the in-memory file content for one path so the sync loop can free memory early.
72+
// @intent prevent the FileInfo map from holding all source bytes after a file has been processed.
73+
// @mutates files[filePath].Content
6974
func releaseContent(files map[string]FileInfo, filePath string) {
7075
info, ok := files[filePath]
7176
if !ok {
@@ -147,6 +152,10 @@ func (s *Syncer) SyncWithExistingStore(ctx context.Context, syncStore Store, fil
147152
return s.syncWithExisting(ctx, syncStore, files, existingFiles)
148153
}
149154

155+
// syncWithExisting performs the actual diff-and-apply pass against the supplied store.
156+
// @intent compare hashes for known files, parse new/changed ones, and remove deleted entries in one pass.
157+
// @sideEffect upserts nodes/edges/annotations and deletes removed files through syncStore.
158+
// @mutates graph nodes, edges, annotations
150159
func (s *Syncer) syncWithExisting(ctx context.Context, syncStore Store, files map[string]FileInfo, existingFiles []string) (*SyncStats, error) {
151160
stats := &SyncStats{}
152161

@@ -242,6 +251,8 @@ func (s *Syncer) syncWithExisting(ctx context.Context, syncStore Store, files ma
242251
return stats, nil
243252
}
244253

254+
// resolveParser picks an extension-specific parser when configured, otherwise the legacy single parser.
255+
// @intent let multi-language projects sync without losing the single-parser fallback for callers using New.
245256
func (s *Syncer) resolveParser(filePath string) Parser {
246257
if len(s.parsers) > 0 {
247258
ext := strings.ToLower(filepath.Ext(filePath))
@@ -252,6 +263,10 @@ func (s *Syncer) resolveParser(filePath string) Parser {
252263
return s.parser
253264
}
254265

266+
// restoreAnnotations re-binds parsed comment blocks to the freshly persisted nodes for one file.
267+
// @intent keep doc comments associated with their owning declarations after incremental reparses.
268+
// @sideEffect upserts annotation rows through the store's annotation writer.
269+
// @mutates graph annotations
255270
func (s *Syncer) restoreAnnotations(ctx context.Context, syncStore Store, filePath string, content []byte, nodes []model.Node, comments []treesitter.CommentBlock, language string) error {
256271
writer, ok := syncStore.(annotationWriter)
257272
if !ok || language == "" {
@@ -298,6 +313,8 @@ func (s *Syncer) restoreAnnotations(ctx context.Context, syncStore Store, filePa
298313
return nil
299314
}
300315

316+
// annotationBindingKey produces a stable lookup key combining qualified name and start line.
317+
// @intent disambiguate overloaded or repeated declarations sharing the same qualified name.
301318
func annotationBindingKey(qualifiedName string, startLine int) string {
302319
return qualifiedName + ":" + strconv.Itoa(startLine)
303320
}

0 commit comments

Comments
 (0)