STET

agent_patch

runs/2026-02-28__13-38-10__gpt-5-3-codex/flux-pr-1087/flux-pr-1087.1-of-1.2026-02-28__13-38-10__gpt-5-3-codex/agent-logs/agent.patch

9185 bytes

Back to adjudication
diff --git a/tmp/agent-patch-flux-pr-1087.1-of-1.2026-02-28__13-38-10__gpt-5-3-codex/app/v2/pkg/engine/datasourcetesting/datasourcetesting.go b/app/v2/pkg/engine/datasourcetesting/datasourcetesting.go
index 9a02e3e..a783615 100644
--- a/tmp/agent-patch-flux-pr-1087.1-of-1.2026-02-28__13-38-10__gpt-5-3-codex/app/v2/pkg/engine/datasourcetesting/datasourcetesting.go
+++ b/app/v2/pkg/engine/datasourcetesting/datasourcetesting.go
@@ -3,17 +3,14 @@ package datasourcetesting
 import (
 	"encoding/json"
 	"fmt"
-	"math/rand"
 	"reflect"
 	"slices"
 	"testing"
-	"time"
 
 	"github.com/kylelemons/godebug/diff"
 	"github.com/kylelemons/godebug/pretty"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
-	"gonum.org/v1/gonum/stat/combin"
 
 	"github.com/wundergraph/graphql-go-tools/v2/pkg/internal/unsafeprinter"
 
@@ -25,6 +22,7 @@ import (
 	"github.com/wundergraph/graphql-go-tools/v2/pkg/engine/postprocess"
 	"github.com/wundergraph/graphql-go-tools/v2/pkg/internal/unsafeparser"
 	"github.com/wundergraph/graphql-go-tools/v2/pkg/operationreport"
+	"github.com/wundergraph/graphql-go-tools/v2/pkg/testing/permutations"
 )
 
 type testOptions struct {
@@ -225,39 +223,23 @@ func RunTestWithVariables(definition, operation, operationName, variables string
 // ShuffleDS randomizes the order of the data sources
 // to ensure that the order doesn't matter
 func ShuffleDS(dataSources []plan.DataSource) []plan.DataSource {
-	rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
-	rnd.Shuffle(len(dataSources), func(i, j int) {
-		dataSources[i], dataSources[j] = dataSources[j], dataSources[i]
-	})
-
-	return dataSources
+	return permutations.Shuffle(dataSources)
 }
 
 func OrderDS(dataSources []plan.DataSource, order []int) (out []plan.DataSource) {
-	out = make([]plan.DataSource, 0, len(dataSources))
-
-	for _, i := range order {
-		out = append(out, dataSources[i])
-	}
-
-	return out
+	return permutations.Order(dataSources, order)
 }
 
 func DataSourcePermutations(dataSources []plan.DataSource) []*Permutation {
-	size := len(dataSources)
-	elementsCount := len(dataSources)
-	list := combin.Permutations(size, elementsCount)
-
-	permutations := make([]*Permutation, 0, len(list))
-
-	for _, v := range list {
-		permutations = append(permutations, &Permutation{
-			Order:       v,
-			DataSources: OrderDS(dataSources, v),
+	perms := permutations.Generate(dataSources)
+	result := make([]*Permutation, 0, len(perms))
+	for _, p := range perms {
+		result = append(result, &Permutation{
+			Order:       p.Order,
+			DataSources: p.Values,
 		})
 	}
-
-	return permutations
+	return result
 }
 
 type Permutation struct {
diff --git a/tmp/agent-patch-flux-pr-1087.1-of-1.2026-02-28__13-38-10__gpt-5-3-codex/app/v2/pkg/engine/plan/datasource_filter_collect_nodes_visitor.go b/app/v2/pkg/engine/plan/datasource_filter_collect_nodes_visitor.go
index 1262c2e..7612c1b 100644
--- a/tmp/agent-patch-flux-pr-1087.1-of-1.2026-02-28__13-38-10__gpt-5-3-codex/app/v2/pkg/engine/plan/datasource_filter_collect_nodes_visitor.go
+++ b/app/v2/pkg/engine/plan/datasource_filter_collect_nodes_visitor.go
@@ -198,7 +198,12 @@ func (f *collectNodesVisitor) isInterfaceObject(typeName string) bool {
 // has disabled entity resolver
 func (f *collectNodesVisitor) allKeysHasDisabledEntityResolver(typeName string) bool {
 	keys := f.dataSource.FederationConfiguration().Keys
-	return !slices.ContainsFunc(keys.FilterByTypeAndResolvability(typeName, false), func(k FederationFieldConfiguration) bool {
+	keyConfigurations := keys.FilterByTypeAndResolvability(typeName, false)
+	if len(keyConfigurations) == 0 {
+		return false
+	}
+
+	return !slices.ContainsFunc(keyConfigurations, func(k FederationFieldConfiguration) bool {
 		return !k.DisableEntityResolver
 	})
 }
diff --git a/app/v2/pkg/engine/plan/datasource_filter_regression_test.go b/app/v2/pkg/engine/plan/datasource_filter_regression_test.go
new file mode 100644
index 0000000..687e06e
--- /dev/null
+++ b/app/v2/pkg/engine/plan/datasource_filter_regression_test.go
@@ -0,0 +1,87 @@
+package plan
+
+import (
+	"testing"
+
+	"github.com/kingledion/go-tools/tree"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestAllKeysHasDisabledEntityResolver(t *testing.T) {
+	t.Run("no keys should not be treated as disabled entity resolver", func(t *testing.T) {
+		visitor := &collectNodesVisitor{
+			dataSource: dsb().
+				Hash(1).
+				RootNode("User", "id").
+				DS(),
+		}
+
+		assert.False(t, visitor.allKeysHasDisabledEntityResolver("User"))
+	})
+
+	t.Run("all keys disabled should be treated as disabled entity resolver", func(t *testing.T) {
+		visitor := &collectNodesVisitor{
+			dataSource: dsb().
+				Hash(1).
+				RootNode("User", "id").
+				KeysMetadata(FederationFieldConfigurations{
+					{TypeName: "User", SelectionSet: "id", DisableEntityResolver: true},
+				}).
+				DS(),
+		}
+
+		assert.True(t, visitor.allKeysHasDisabledEntityResolver("User"))
+	})
+
+	t.Run("at least one enabled key should not be treated as disabled entity resolver", func(t *testing.T) {
+		visitor := &collectNodesVisitor{
+			dataSource: dsb().
+				Hash(1).
+				RootNode("User", "id").
+				KeysMetadata(FederationFieldConfigurations{
+					{TypeName: "User", SelectionSet: "id", DisableEntityResolver: true},
+					{TypeName: "User", SelectionSet: "email", DisableEntityResolver: false},
+				}).
+				DS(),
+		}
+
+		assert.False(t, visitor.allKeysHasDisabledEntityResolver("User"))
+	})
+}
+
+func TestSelectUniqNodeParentsUpToRootNodeStopsAtExternalParent(t *testing.T) {
+	nodes := NewNodeSuggestionsWithSize(3)
+	nodes.items = []*NodeSuggestion{
+		{
+			FieldRef:       1,
+			DataSourceHash: 11,
+			IsRootNode:     true,
+		},
+		{
+			FieldRef:       2,
+			DataSourceHash: 11,
+			IsExternal:     true,
+			IsProvided:     false,
+		},
+		{
+			FieldRef:       3,
+			DataSourceHash: 11,
+		},
+	}
+
+	responseTree := tree.Empty[[]int]()
+	responseTree.Add(treeRootID, 0, nil)
+	responseTree.Add(TreeNodeID(1), treeRootID, []int{0})
+	responseTree.Add(TreeNodeID(2), TreeNodeID(1), []int{1})
+	responseTree.Add(TreeNodeID(3), TreeNodeID(2), []int{2})
+	nodes.responseTree = *responseTree
+
+	filter := &DataSourceFilter{
+		nodes: nodes,
+	}
+
+	filter.selectUniqNodeParentsUpToRootNode(2)
+
+	assert.False(t, nodes.items[1].Selected)
+	assert.False(t, nodes.items[0].Selected)
+}
diff --git a/tmp/agent-patch-flux-pr-1087.1-of-1.2026-02-28__13-38-10__gpt-5-3-codex/app/v2/pkg/engine/plan/datasource_filter_visitor.go b/app/v2/pkg/engine/plan/datasource_filter_visitor.go
index f96b8e8..df2a1f8 100644
--- a/tmp/agent-patch-flux-pr-1087.1-of-1.2026-02-28__13-38-10__gpt-5-3-codex/app/v2/pkg/engine/plan/datasource_filter_visitor.go
+++ b/app/v2/pkg/engine/plan/datasource_filter_visitor.go
@@ -206,6 +206,13 @@ func (f *DataSourceFilter) selectUniqNodeParentsUpToRootNode(i int) {
 		if !ok {
 			break
 		}
+
+		// External nodes are owned by a different datasource at this path, so parent selection
+		// must not continue through them.
+		if f.nodes.items[parentIdx].IsExternal && !f.nodes.items[parentIdx].IsProvided {
+			break
+		}
+
 		f.nodes.items[parentIdx].selectWithReason(ReasonStage1SameSourceParent, f.enableSelectionReasons)
 
 		current = parentIdx
diff --git a/app/v2/pkg/testing/permutations/permutations.go b/app/v2/pkg/testing/permutations/permutations.go
new file mode 100644
index 0000000..10e7649
--- /dev/null
+++ b/app/v2/pkg/testing/permutations/permutations.go
@@ -0,0 +1,49 @@
+package permutations
+
+import (
+	"math/rand"
+	"time"
+
+	"gonum.org/v1/gonum/stat/combin"
+)
+
+type Permutation[T any] struct {
+	Order  []int
+	Values []T
+}
+
+// Shuffle randomizes the order of elements in place and returns the same slice.
+func Shuffle[T any](items []T) []T {
+	rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
+	rnd.Shuffle(len(items), func(i, j int) {
+		items[i], items[j] = items[j], items[i]
+	})
+
+	return items
+}
+
+func Order[T any](items []T, order []int) (out []T) {
+	out = make([]T, 0, len(items))
+
+	for _, i := range order {
+		out = append(out, items[i])
+	}
+
+	return out
+}
+
+func Generate[T any](items []T) []*Permutation[T] {
+	size := len(items)
+	elementsCount := len(items)
+	list := combin.Permutations(size, elementsCount)
+
+	permutations := make([]*Permutation[T], 0, len(list))
+	for _, v := range list {
+		permutations = append(permutations, &Permutation[T]{
+			Order:  v,
+			Values: Order(items, v),
+		})
+	}
+
+	return permutations
+}
diff --git a/app/v2/pkg/testing/permutations/permutations_test.go b/app/v2/pkg/testing/permutations/permutations_test.go
new file mode 100644
index 0000000..419937d
--- /dev/null
+++ b/app/v2/pkg/testing/permutations/permutations_test.go
@@ -0,0 +1,20 @@
+package permutations
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestGenerate(t *testing.T) {
+	perms := Generate([]string{"a", "b", "c"})
+
+	assert.Len(t, perms, 6)
+
+	assert.Equal(t, []int{0, 1, 2}, perms[0].Order)
+	assert.Equal(t, []string{"a", "b", "c"}, perms[0].Values)
+}
+
+func TestOrder(t *testing.T) {
+	assert.Equal(t, []int{3, 1, 2}, Order([]int{1, 2, 3}, []int{2, 0, 1}))
+}