STET

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

Back to adjudication
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) {