STET

agent_patch

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

12565 bytes

Back to adjudication
diff --git a/tmp/agent-patch-flux-pr-1087.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/datasourcetesting/datasourcetesting.go b/app/v2/pkg/engine/datasourcetesting/datasourcetesting.go
index 9a02e3e..a2505bf 100644
--- a/tmp/agent-patch-flux-pr-1087.1-of-1.2026-02-28__13-38-10__gpt-5-4/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 {
@@ -76,7 +74,7 @@ func RunWithPermutations(t *testing.T, definition, operation, operationName stri
 		permutation := dataSourcePermutations[i]
 		t.Run(fmt.Sprintf("permutation %v", permutation.Order), func(t *testing.T) {
 			permutationPlanConfiguration := config
-			permutationPlanConfiguration.DataSources = permutation.DataSources
+			permutationPlanConfiguration.DataSources = permutation.Items
 
 			t.Run("run", RunTest(
 				definition,
@@ -101,7 +99,7 @@ func RunWithPermutationsVariants(t *testing.T, definition, operation, operationN
 		permutation := dataSourcePermutations[i]
 		t.Run(fmt.Sprintf("permutation %v", permutation.Order), func(t *testing.T) {
 			permutationPlanConfiguration := config
-			permutationPlanConfiguration.DataSources = permutation.DataSources
+			permutationPlanConfiguration.DataSources = permutation.Items
 
 			t.Run("run", RunTest(
 				definition,
@@ -225,42 +223,13 @@ 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
-}
-
-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),
-		})
-	}
-
-	return permutations
+	return permutations.Order(dataSources, order)
 }
 
-type Permutation struct {
-	Order       []int
-	DataSources []plan.DataSource
+func DataSourcePermutations(dataSources []plan.DataSource) []permutations.Permutation[plan.DataSource] {
+	return permutations.Generate(dataSources)
 }
diff --git a/tmp/agent-patch-flux-pr-1087.1-of-1.2026-02-28__13-38-10__gpt-5-4/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..d292ba2 100644
--- a/tmp/agent-patch-flux-pr-1087.1-of-1.2026-02-28__13-38-10__gpt-5-4/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 {
+	typeKeys := keys.FilterByTypeAndResolvability(typeName, false)
+	if len(typeKeys) == 0 {
+		return false
+	}
+
+	return !slices.ContainsFunc(typeKeys, func(k FederationFieldConfiguration) bool {
 		return !k.DisableEntityResolver
 	})
 }
diff --git a/tmp/agent-patch-flux-pr-1087.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/plan/datasource_filter_visitor.go b/app/v2/pkg/engine/plan/datasource_filter_visitor.go
index f96b8e8..955d8b3 100644
--- a/tmp/agent-patch-flux-pr-1087.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/plan/datasource_filter_visitor.go
+++ b/app/v2/pkg/engine/plan/datasource_filter_visitor.go
@@ -206,6 +206,11 @@ func (f *DataSourceFilter) selectUniqNodeParentsUpToRootNode(i int) {
 		if !ok {
 			break
 		}
+
+		if f.nodes.items[parentIdx].IsExternal && !f.nodes.items[parentIdx].IsProvided {
+			break
+		}
+
 		f.nodes.items[parentIdx].selectWithReason(ReasonStage1SameSourceParent, f.enableSelectionReasons)
 
 		current = parentIdx
@@ -421,6 +426,10 @@ func (f *DataSourceFilter) findPossibleParents(i int) (parentIds []int) {
 
 	parentIdx, _ := f.nodes.parentNodeOnSameSource(i)
 	for parentIdx != -1 {
+		if f.nodes.items[parentIdx].IsExternal && !f.nodes.items[parentIdx].IsProvided {
+			break
+		}
+
 		nodesIdsToSelect = append(nodesIdsToSelect, parentIdx)
 
 		// for the parent node we are checking if it is a root node and has enabled entity resolver.
diff --git a/tmp/agent-patch-flux-pr-1087.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 89b48b8..7bbcdb2 100644
--- a/tmp/agent-patch-flux-pr-1087.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
@@ -2,10 +2,8 @@ package plan
 
 import (
 	"fmt"
-	"math/rand"
 	"slices"
 	"testing"
-	"time"
 
 	"github.com/kylelemons/godebug/pretty"
 	"github.com/stretchr/testify/assert"
@@ -13,6 +11,7 @@ import (
 	"github.com/wundergraph/graphql-go-tools/v2/pkg/astvalidation"
 	"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 dsBuilder struct {
@@ -960,25 +959,147 @@ func TestFindBestDataSourceSet(t *testing.T) {
 	}
 }
 
-// shuffleDS randomizes the order of the data sources
-// to ensure that the order doesn't matter
-func shuffleDS(dataSources []DataSource) []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]
+func TestCollectNodesVisitor_AllKeysHasDisabledEntityResolver(t *testing.T) {
+	t.Run("no keys", func(t *testing.T) {
+		visitor := &collectNodesVisitor{
+			dataSource: dsb().Hash(11).Schema(`
+				type User {
+					name: String!
+				}
+			`).RootNode("User", "name").DS(),
+		}
+
+		assert.False(t, visitor.allKeysHasDisabledEntityResolver("User"))
 	})
 
-	return dataSources
+	t.Run("all keys disabled", func(t *testing.T) {
+		visitor := &collectNodesVisitor{
+			dataSource: dsb().Hash(11).Schema(`
+				type User {
+					id: ID!
+				}
+			`).RootNode("User", "id").WithMetadata(func(data *FederationMetaData) {
+				data.Keys = FederationFieldConfigurations{
+					{
+						TypeName:              "User",
+						SelectionSet:          "id",
+						DisableEntityResolver: true,
+					},
+				}
+			}).DS(),
+		}
+
+		assert.True(t, visitor.allKeysHasDisabledEntityResolver("User"))
+	})
 }
 
 func orderDS(dataSources []DataSource, order []int) (out []DataSource) {
-	out = make([]DataSource, 0, len(dataSources))
+	return permutations.Order(dataSources, order)
+}
+
+// shuffleDS randomizes the order of the data sources
+// to ensure that the order doesn't matter
+func shuffleDS(dataSources []DataSource) []DataSource {
+	return permutations.Shuffle(dataSources)
+}
+
+func TestFindBestDataSourceSet_DoesNotSelectExternalParentsOfUniqueNode(t *testing.T) {
+	definition := unsafeparser.ParseGraphqlDocumentStringWithBaseSchema(`
+		type Query {
+			me: User!
+		}
+
+		type User {
+			id: ID!
+			details: Details!
+		}
 
-	for _, i := range order {
-		out = append(out, dataSources[i])
+		type Details {
+			age: Int!
+		}
+	`)
+	operation := unsafeparser.ParseGraphqlDocumentString(`
+		query {
+			me {
+				details {
+					age
+				}
+			}
+		}
+	`)
+	report := operationreport.Report{}
+
+	astvalidation.DefaultOperationValidator().Validate(&operation, &definition, &report)
+	if report.HasErrors() {
+		t.Fatal(report.Error())
+	}
+
+	ds1 := dsb().Hash(11).Schema(`
+		type Query {
+			me: User!
+		}
+
+		type User @key(fields: "id") {
+			id: ID!
+			details: Details!
+		}
+
+		type Details {
+			noop: Boolean
+		}
+	`).RootNode("Query", "me").
+		RootNode("User", "id", "details").
+		DS()
+
+	ds2Builder := dsb().Hash(22).Schema(`
+		type User @key(fields: "id") {
+			id: ID!
+			details: Details! @external
+		}
+
+		type Details {
+			age: Int!
+		}
+	`).RootNode("User", "id").
+		ChildNode("Details", "age")
+	ds2Builder.ds.RootNodes[0].ExternalFieldNames = []string{"details"}
+	ds2 := ds2Builder.DS()
+
+	filter := NewDataSourceFilter(&operation, &definition, &report)
+	filter.EnableSelectionReasons()
+	filter.dataSources = []DataSource{ds1, ds2}
+
+	planned, _ := filter.findBestDataSourceSet(nil, nil)
+	if report.HasErrors() {
+		t.Fatal(report.Error())
+	}
+
+	var (
+		ds1DetailsSelected bool
+		ds2DetailsSelected bool
+		ds2AgeSelected     bool
+	)
+
+	for _, item := range planned.items {
+		if !item.Selected {
+			continue
+		}
+
+		if item.TypeName == "User" && item.FieldName == "details" && item.DataSourceHash == 11 {
+			ds1DetailsSelected = true
+		}
+		if item.TypeName == "User" && item.FieldName == "details" && item.DataSourceHash == 22 {
+			ds2DetailsSelected = true
+		}
+		if item.TypeName == "Details" && item.FieldName == "age" && item.DataSourceHash == 22 {
+			ds2AgeSelected = true
+			assert.Equal(t, []string{ReasonStage1Unique}, item.SelectionReasons)
+		}
 	}
 
-	return out
+	assert.True(t, ds1DetailsSelected)
+	assert.False(t, ds2DetailsSelected)
+	assert.True(t, ds2AgeSelected)
 }
 
 const shareableDefinition = `
diff --git a/app/v2/pkg/testing/permutations/permutations.go b/app/v2/pkg/testing/permutations/permutations.go
new file mode 100644
index 0000000..a18bd75
--- /dev/null
+++ b/app/v2/pkg/testing/permutations/permutations.go
@@ -0,0 +1,47 @@
+package permutations
+
+import (
+	"math/rand"
+	"time"
+
+	"gonum.org/v1/gonum/stat/combin"
+)
+
+type Permutation[T any] struct {
+	Order []int
+	Items []T
+}
+
+func Shuffle[T any](items []T) []T {
+	shuffled := append([]T(nil), items...)
+	rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
+	rnd.Shuffle(len(shuffled), func(i, j int) {
+		shuffled[i], shuffled[j] = shuffled[j], shuffled[i]
+	})
+
+	return shuffled
+}
+
+func Order[T any](items []T, order []int) []T {
+	ordered := make([]T, 0, len(items))
+
+	for _, i := range order {
+		ordered = append(ordered, items[i])
+	}
+
+	return ordered
+}
+
+func Generate[T any](items []T) []Permutation[T] {
+	list := combin.Permutations(len(items), len(items))
+	permutations := make([]Permutation[T], 0, len(list))
+
+	for _, order := range list {
+		permutations = append(permutations, Permutation[T]{
+			Order: order,
+			Items: Order(items, order),
+		})
+	}
+
+	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..537d683
--- /dev/null
+++ b/app/v2/pkg/testing/permutations/permutations_test.go
@@ -0,0 +1,17 @@
+package permutations
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestGenerate(t *testing.T) {
+	perms := Generate([]int{1, 2, 3})
+
+	assert.Len(t, perms, 6)
+	assert.Equal(t, []int{0, 1, 2}, perms[0].Order)
+	assert.Equal(t, []int{1, 2, 3}, perms[0].Items)
+	assert.Equal(t, []int{2, 1, 0}, perms[len(perms)-1].Order)
+	assert.Equal(t, []int{3, 2, 1}, perms[len(perms)-1].Items)
+}