Skip to content

Commit a2782ef

Browse files
tae2089claude
andcommitted
Skip dangling fallback edges instead of dropping the whole page
find_suspect_fallback_edges returned an empty zero-value Result with a nil error whenever any edge endpoint node was missing, silently hiding every other suspect on the page. Skip edges with missing endpoints. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 3ab9d09 commit a2782ef

2 files changed

Lines changed: 44 additions & 2 deletions

File tree

internal/analysis/fallback/service.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,13 +86,17 @@ func (s *Service) FindSuspectsPage(ctx context.Context, opts Options) (Result, e
8686
results := make([]SuspectEdge, 0, len(edges))
8787
for _, edge := range edges {
8888
source, err := s.store.GetNodeByID(ctx, edge.FromNodeID)
89-
if err != nil || source == nil {
89+
if err != nil {
9090
return Result{}, err
9191
}
9292
target, err := s.store.GetNodeByID(ctx, edge.ToNodeID)
93-
if err != nil || target == nil {
93+
if err != nil {
9494
return Result{}, err
9595
}
96+
// A dangling edge (endpoint node missing) must not silently drop the whole page.
97+
if source == nil || target == nil {
98+
continue
99+
}
96100
sourceAnn, err := s.store.GetAnnotation(ctx, source.ID)
97101
if err != nil {
98102
return Result{}, err

internal/analysis/fallback/service_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,44 @@ func TestSuspectFallbackEdges_FlagsDisjointIntentAndDomainRule(t *testing.T) {
6363
}
6464
}
6565

66+
func TestSuspectFallbackEdges_SkipsEdgeWithMissingEndpointInsteadOfDroppingPage(t *testing.T) {
67+
db := setupFallbackDB(t)
68+
ctx := context.Background()
69+
store := gormstore.New(db)
70+
71+
source := model.Node{QualifiedName: "pkg.A", Kind: model.NodeKindFunction, Name: "A", FilePath: "a.go", StartLine: 1, EndLine: 2, Language: "go"}
72+
target := model.Node{QualifiedName: "pkg.B", Kind: model.NodeKindFunction, Name: "B", FilePath: "b.go", StartLine: 1, EndLine: 2, Language: "go"}
73+
if err := store.UpsertNodes(ctx, []model.Node{source, target}); err != nil {
74+
t.Fatal(err)
75+
}
76+
sourceNode, _ := store.GetNode(ctx, "pkg.A")
77+
targetNode, _ := store.GetNode(ctx, "pkg.B")
78+
if err := store.UpsertEdges(ctx, []model.Edge{
79+
// dangling edge: endpoint node id does not exist
80+
{FromNodeID: 99999, ToNodeID: targetNode.ID, Kind: model.EdgeKindFallbackCalls, Fingerprint: "a-dangling"},
81+
{FromNodeID: sourceNode.ID, ToNodeID: targetNode.ID, Kind: model.EdgeKindFallbackCalls, Fingerprint: "b-valid"},
82+
}); err != nil {
83+
t.Fatal(err)
84+
}
85+
if err := store.UpsertAnnotation(ctx, &model.Annotation{NodeID: sourceNode.ID, Summary: "auth", Tags: []model.DocTag{{Kind: model.TagIntent, Value: "verify credentials", Ordinal: 0}}}); err != nil {
86+
t.Fatal(err)
87+
}
88+
if err := store.UpsertAnnotation(ctx, &model.Annotation{NodeID: targetNode.ID, Summary: "billing", Tags: []model.DocTag{{Kind: model.TagIntent, Value: "render invoice", Ordinal: 0}}}); err != nil {
89+
t.Fatal(err)
90+
}
91+
92+
results, err := New(db, store).FindSuspects(ctx, Options{})
93+
if err != nil {
94+
t.Fatal(err)
95+
}
96+
if len(results) != 1 {
97+
t.Fatalf("dangling edge must be skipped, not drop the whole page: got %d results", len(results))
98+
}
99+
if results[0].Source.QualifiedName != "pkg.A" {
100+
t.Fatalf("expected surviving valid edge, got %+v", results[0])
101+
}
102+
}
103+
66104
func TestSuspectFallbackEdges_IgnoresOverlappingAnnotationContext(t *testing.T) {
67105
db := setupFallbackDB(t)
68106
ctx := context.Background()

0 commit comments

Comments
 (0)