Skip to content

Commit 797ca05

Browse files
committed
fix: exclude self-loops from traversal context deps
1 parent 8d8882d commit 797ca05

2 files changed

Lines changed: 23 additions & 3 deletions

File tree

lib/sdk/server-ai/src/main/java/com/launchdarkly/sdk/server/ai/AgentGraphDefinition.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -207,12 +207,12 @@ public void traverse(BiFunction<AgentGraphNode, Map<String, Object>, Object> fn,
207207
if (next == null) {
208208
next = lowestDegree(order, visited, indeg);
209209
}
210-
visited.add(next);
211210

211+
// Accumulate deps before marking visited so a self-loop does not count as its own ancestor.
212212
Set<String> anc = new HashSet<>();
213213
for (AgentGraphNode parent : getParentNodes(next)) {
214214
String pk = parent.getKey();
215-
if (!visited.contains(pk) || !reachable.contains(pk) || pk.equals(next)) {
215+
if (!visited.contains(pk) || !reachable.contains(pk)) {
216216
continue;
217217
}
218218
anc.add(pk);
@@ -222,6 +222,7 @@ public void traverse(BiFunction<AgentGraphNode, Map<String, Object>, Object> fn,
222222
}
223223
}
224224
ancestors.put(next, anc);
225+
visited.add(next);
225226

226227
AgentGraphNode nextNode = getNode(next);
227228
results.put(next, fn.apply(nextNode, scopedCtx(ctx, results, anc)));
@@ -282,8 +283,8 @@ public void reverseTraverse(BiFunction<AgentGraphNode, Map<String, Object>, Obje
282283
if (next == null) {
283284
next = lowestDegreeNonRoot(order, visited, outdeg, rootKey);
284285
}
285-
visited.add(next);
286286

287+
// Accumulate deps before marking visited so a self-loop does not count as its own descendant.
287288
Set<String> desc = new HashSet<>();
288289
AgentGraphNode nextNode = getNode(next);
289290
if (nextNode != null) {
@@ -300,6 +301,7 @@ public void reverseTraverse(BiFunction<AgentGraphNode, Map<String, Object>, Obje
300301
}
301302
}
302303
descendants.put(next, desc);
304+
visited.add(next);
303305
results.put(next, fn.apply(nextNode, scopedCtx(ctx, results, desc)));
304306

305307
for (AgentGraphNode parent : getParentNodes(next)) {

lib/sdk/server-ai/src/test/java/com/launchdarkly/sdk/server/ai/AgentGraphDefinitionTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,24 @@ public void reverseTraverseHandlesCyclesSafely() {
417417
assertThat(new HashSet<>(visited), containsInAnyOrder("a", "b"));
418418
}
419419

420+
@Test
421+
public void selfLoopIsNotIncludedInOwnContext() {
422+
// a → b → b (self-loop on b)
423+
AgentGraphDefinition graph = buildEnabled("a",
424+
new String[][]{{"a", "b"}, {"b", "b"}}, "a", "b");
425+
Map<String, Object> initial = new HashMap<>();
426+
initial.put("seed", 1);
427+
428+
Map<String, Set<String>> fwd = captureContextKeys(graph, false, initial);
429+
assertThat(fwd.get("b"), containsInAnyOrder("seed", "a"));
430+
assertThat(fwd.get("b").contains("b"), is(false));
431+
432+
Map<String, Set<String>> rev = captureContextKeys(graph, true, initial);
433+
assertThat(rev.get("b"), containsInAnyOrder("seed"));
434+
assertThat(rev.get("b").contains("b"), is(false));
435+
assertThat(rev.get("a"), containsInAnyOrder("seed", "b"));
436+
}
437+
420438
@Test
421439
public void reverseTraverseIsNoOpWhenDisabled() {
422440
AgentGraphDefinition graph = new AgentGraphDefinition(

0 commit comments

Comments
 (0)