agent_patch
runs/2026-02-28__13-38-10__gpt-5-4/flux-pr-1338/flux-pr-1338.1-of-1.2026-02-28__13-38-10__gpt-5-4/agent-logs/agent.patch
7955 bytes
diff --git a/tmp/agent-patch-flux-pr-1338.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/plan/datasource_filter_node_suggestions.go b/app/v2/pkg/engine/plan/datasource_filter_node_suggestions.go
index 9722949..3d951b5 100644
--- a/tmp/agent-patch-flux-pr-1338.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/plan/datasource_filter_node_suggestions.go
+++ b/app/v2/pkg/engine/plan/datasource_filter_node_suggestions.go
@@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"iter"
+ "slices"
"github.com/kingledion/go-tools/tree"
"github.com/phf/go-queue/queue"
@@ -156,9 +157,35 @@ func (f *NodeSuggestions) RemoveTreeNodeChilds(fieldRef int) {
return
}
+ f.removeTreeNodeSuggestions(node.GetChildren())
node.ReplaceChildren()
}
+func (f *NodeSuggestions) removeTreeNodeSuggestions(nodes []tree.Node[[]int]) {
+ for _, node := range nodes {
+ f.removeTreeNodeSuggestions(node.GetChildren())
+
+ itemIDs := node.GetData()
+ for _, itemID := range itemIDs {
+ suggestion := f.items[itemID]
+ suggestion.unselect()
+
+ items := f.pathSuggestions[suggestion.Path]
+ items = slices.DeleteFunc(items, func(item *NodeSuggestion) bool {
+ return item == suggestion
+ })
+ if len(items) == 0 {
+ delete(f.pathSuggestions, suggestion.Path)
+ continue
+ }
+ f.pathSuggestions[suggestion.Path] = items
+ }
+
+ node.SetData(nil)
+ node.ReplaceChildren()
+ }
+}
+
func (f *NodeSuggestions) addSuggestion(node *NodeSuggestion) (suggestionIdx int) {
f.items = append(f.items, node)
return len(f.items) - 1
diff --git a/tmp/agent-patch-flux-pr-1338.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/plan/datasource_filter_visitor_test.go b/app/v2/pkg/engine/plan/datasource_filter_visitor_test.go
index 24e691a..64d636e 100644
--- a/tmp/agent-patch-flux-pr-1338.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/plan/datasource_filter_visitor_test.go
+++ b/app/v2/pkg/engine/plan/datasource_filter_visitor_test.go
@@ -6,6 +6,7 @@ import (
"strings"
"testing"
+ "github.com/kingledion/go-tools/tree"
"github.com/kylelemons/godebug/pretty"
"github.com/stretchr/testify/assert"
@@ -1108,6 +1109,50 @@ func TestFindBestDataSourceSet(t *testing.T) {
}
}
+func TestNodeSuggestions_RemoveTreeNodeChildsRemovesOrphanedSuggestions(t *testing.T) {
+ nodes := NewNodeSuggestions()
+ nodes.items = []*NodeSuggestion{
+ {
+ FieldRef: 1,
+ Path: "query.account",
+ TypeName: "Query",
+ FieldName: "account",
+ DataSourceHash: 1,
+ Selected: true,
+ treeNodeId: TreeNodeID(1),
+ SelectionReasons: []string{"selected"},
+ },
+ {
+ FieldRef: 2,
+ Path: "query.account.name",
+ TypeName: "Account",
+ FieldName: "name",
+ DataSourceHash: 1,
+ Selected: true,
+ treeNodeId: TreeNodeID(2),
+ SelectionReasons: []string{"selected"},
+ },
+ }
+ nodes.pathSuggestions = map[string][]*NodeSuggestion{
+ "query.account": {nodes.items[0]},
+ "query.account.name": {nodes.items[1]},
+ }
+
+ responseTree := tree.Empty[[]int]()
+ _, _ = responseTree.Add(treeRootID, 0, nil)
+ _, _ = responseTree.Add(TreeNodeID(1), treeRootID, []int{0})
+ _, _ = responseTree.Add(TreeNodeID(2), TreeNodeID(1), []int{1})
+ nodes.responseTree = *responseTree
+
+ nodes.RemoveTreeNodeChilds(1)
+
+ parentNode, ok := nodes.responseTree.Find(TreeNodeID(1))
+ assert.True(t, ok)
+ assert.Empty(t, parentNode.GetChildren())
+ assert.Nil(t, nodes.SuggestionsForPath("Account", "name", "query.account.name"))
+ assert.False(t, nodes.items[1].Selected)
+}
+
const shareableDefinition = `
type User {
id: ID!
diff --git a/tmp/agent-patch-flux-pr-1338.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/plan/node_selection_visitor.go b/app/v2/pkg/engine/plan/node_selection_visitor.go
index 0345af2..8e79d04 100644
--- a/tmp/agent-patch-flux-pr-1338.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/plan/node_selection_visitor.go
+++ b/app/v2/pkg/engine/plan/node_selection_visitor.go
@@ -229,6 +229,9 @@ func (c *nodeSelectionVisitor) EnterField(fieldRef int) {
// check if field selections are abstract and needs rewrites
c.rewriteSelectionSetHavingAbstractFragments(fieldRef, ds)
}
+
+ c.processPendingFieldRequirements(c.currentSelectionSet())
+ c.processPendingKeyRequirements(c.currentSelectionSet())
}
func (c *nodeSelectionVisitor) LeaveField(ref int) {
diff --git a/tmp/agent-patch-flux-pr-1338.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/plan/path_builder_visitor.go b/app/v2/pkg/engine/plan/path_builder_visitor.go
index 2152cf8..8f170b8 100644
--- a/tmp/agent-patch-flux-pr-1338.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/plan/path_builder_visitor.go
+++ b/app/v2/pkg/engine/plan/path_builder_visitor.go
@@ -458,6 +458,7 @@ func (c *pathBuilderVisitor) EnterField(fieldRef int) {
suggestions := c.nodeSuggestions.SuggestionsForPath(typeName, fieldName, currentPath)
shareable := len(suggestions) > 1
+ plannedOnAnySuggestion := false
for _, suggestion := range suggestions {
dsIdx := slices.IndexFunc(c.dataSources, func(d DataSource) bool {
return d.Hash() == suggestion.DataSourceHash
@@ -469,17 +470,17 @@ func (c *pathBuilderVisitor) EnterField(fieldRef int) {
ds := c.dataSources[dsIdx]
if !c.couldPlanField(fieldRef, ds.Hash()) {
- c.handleMissingPath(false, typeName, fieldName, currentPath, shareable)
+ continue
+ }
- // if we could not plan the field, we should skip walking into it
- // as the dependencies conditions are tight to this field,
- // and we could mistakenly plan the nested fields on this datasource without current field
- // It could happen when there are the same field as current on another datasource, and it is allowed to plan it
- c.walker.SkipNode()
- return
+ if c.handlePlanningField(fieldRef, typeName, fieldName, currentPath, parentPath, precedingParentPath, suggestion, ds) {
+ plannedOnAnySuggestion = true
}
+ }
- c.handlePlanningField(fieldRef, typeName, fieldName, currentPath, parentPath, precedingParentPath, suggestion, ds, shareable)
+ c.handleMissingPath(plannedOnAnySuggestion, typeName, fieldName, currentPath, shareable)
+ if !plannedOnAnySuggestion {
+ return
}
// we should update response path and array fields only when we are able to plan - so field is not skipped
@@ -493,7 +494,7 @@ func (c *pathBuilderVisitor) EnterField(fieldRef int) {
c.pushResponsePath(fieldRef, fieldAliasOrName)
}
-func (c *pathBuilderVisitor) handlePlanningField(fieldRef int, typeName, fieldName, currentPath, parentPath, precedingParentPath string, suggestion *NodeSuggestion, ds DataSource, shareable bool) {
+func (c *pathBuilderVisitor) handlePlanningField(fieldRef int, typeName, fieldName, currentPath, parentPath, precedingParentPath string, suggestion *NodeSuggestion, ds DataSource) (planned bool) {
plannedOnPlannerIds := c.fieldsPlannedOn[fieldRef]
if slices.ContainsFunc(plannedOnPlannerIds, func(plannerIdx int) bool {
@@ -502,14 +503,13 @@ func (c *pathBuilderVisitor) handlePlanningField(fieldRef int, typeName, fieldNa
// when we have already planned the field on the same datasource as was suggested
// we do not need to try to plan it again
// if there will be multiple suggestions for the same field, they will be on a different datasources
- return
+ return true
}
isMutationRoot := c.isMutationRoot(currentPath)
var (
plannerIdx int
- planned bool
)
if isMutationRoot {
@@ -526,8 +526,7 @@ func (c *pathBuilderVisitor) handlePlanningField(fieldRef int, typeName, fieldNa
c.addFieldDependencies(fieldRef, typeName, fieldName, plannerIdx)
c.addRootField(fieldRef, plannerIdx)
}
-
- c.handleMissingPath(planned, typeName, fieldName, currentPath, shareable)
+ return planned
}
func (c *pathBuilderVisitor) couldPlanField(fieldRef int, dsHash DSHash) (ok bool) {