agent_patch
runs/2026-02-28__13-38-10__gpt-5-3-codex/flux-pr-1184/flux-pr-1184.1-of-1.2026-02-28__13-38-10__gpt-5-3-codex/agent-logs/agent.patch
17482 bytes
diff --git a/tmp/agent-patch-flux-pr-1184.1-of-1.2026-02-28__13-38-10__gpt-5-3-codex/app/v2/pkg/engine/plan/abstract_selection_rewriter.go b/app/v2/pkg/engine/plan/abstract_selection_rewriter.go
index 3484ce9..2a460a1 100644
--- a/tmp/agent-patch-flux-pr-1184.1-of-1.2026-02-28__13-38-10__gpt-5-3-codex/app/v2/pkg/engine/plan/abstract_selection_rewriter.go
+++ b/app/v2/pkg/engine/plan/abstract_selection_rewriter.go
@@ -177,6 +177,10 @@ func (r *fieldSelectionRewriter) unionFieldSelectionNeedsRewrite(selectionSetInf
return true
}
+ if r.unionFragmentsRequiresCleanup(selectionSetInfo.inlineFragmentsOnUnions, unionTypeNames) {
+ return true
+ }
+
// when we do not have fragments on objects, but only on interfaces
// we need to check that all entities implementing each interface have a root node with the requested fields
// e.g. { ... on Interface { a } }
@@ -221,6 +225,10 @@ func (r *fieldSelectionRewriter) rewriteUnionSelection(fieldRef int, fieldInfo s
// we need to recursively flatten nested fragments on interfaces
r.flattenFragmentOnInterface(inlineFragmentOnInterface.selectionSetInfo, inlineFragmentOnInterface.typeNamesImplementingInterface, unionTypeNames, &newSelectionRefs)
}
+ for _, inlineFragmentOnUnion := range fieldInfo.inlineFragmentsOnUnions {
+ // flatten nested fragments on unions into concrete type fragments
+ r.flattenFragmentOnUnion(inlineFragmentOnUnion.selectionSetInfo, inlineFragmentOnUnion.unionMemberTypeNamesInCurrentDS, unionTypeNames, &newSelectionRefs)
+ }
// filter existing fragments by type names exists in the current datasource
// TODO: do not need to iterate 2 times in filter and here
@@ -360,6 +368,12 @@ func (r *fieldSelectionRewriter) interfaceFieldSelectionNeedsRewrite(selectionSe
}
}
+ if selectionSetInfo.hasInlineFragmentsOnUnions {
+ if r.unionFragmentsRequiresCleanup(selectionSetInfo.inlineFragmentsOnUnions, interfaceTypeNames) {
+ return true
+ }
+ }
+
if selectionSetInfo.hasInlineFragmentsOnInterfaces && selectionSetInfo.hasInlineFragmentsOnObjects {
if len(entitiesWithoutFragment) > 0 {
if !r.allEntitiesImplementsInterfaces(selectionSetInfo.inlineFragmentsOnInterfaces, entitiesWithoutFragment) {
@@ -438,7 +452,71 @@ func (r *fieldSelectionRewriter) flattenFragmentOnInterface(selectionSetInfo sel
// in case of interfaces the only thing which is matter is an interception of implementing types
// and parent allowed types
- r.flattenFragmentOnInterface(inlineFragmentInfo.selectionSetInfo, inlineFragmentInfo.typeNamesImplementingInterface, filteredImplementingTypes, selectionRefs)
+ typeNamesImplementingInterfaceInCurrentDS := inlineFragmentInfo.typeNamesImplementingInterfaceInCurrentDS
+ if len(typeNamesImplementingInterfaceInCurrentDS) == 0 {
+ typeNamesImplementingInterfaceInCurrentDS = inlineFragmentInfo.typeNamesImplementingInterface
+ }
+
+ r.flattenFragmentOnInterface(inlineFragmentInfo.selectionSetInfo, typeNamesImplementingInterfaceInCurrentDS, filteredImplementingTypes, selectionRefs)
+ }
+
+ for _, inlineFragmentInfo := range selectionSetInfo.inlineFragmentsOnUnions {
+ unionMemberTypeNamesInCurrentDS := inlineFragmentInfo.unionMemberTypeNamesInCurrentDS
+ if len(unionMemberTypeNamesInCurrentDS) == 0 {
+ unionMemberTypeNamesInCurrentDS = inlineFragmentInfo.unionMemberTypeNames
+ }
+
+ r.flattenFragmentOnUnion(inlineFragmentInfo.selectionSetInfo, unionMemberTypeNamesInCurrentDS, filteredImplementingTypes, selectionRefs)
+ }
+}
+
+func (r *fieldSelectionRewriter) flattenFragmentOnUnion(selectionSetInfo selectionSetInfo, unionMemberTypeNamesInCurrentDS []string, allowedTypeNames []string, selectionRefs *[]int) {
+ if len(unionMemberTypeNamesInCurrentDS) == 0 {
+ return
+ }
+
+ filteredUnionMemberTypes := make([]string, 0, len(unionMemberTypeNamesInCurrentDS))
+ for _, typeName := range unionMemberTypeNamesInCurrentDS {
+ if slices.Contains(allowedTypeNames, typeName) {
+ filteredUnionMemberTypes = append(filteredUnionMemberTypes, typeName)
+ }
+ }
+
+ if len(filteredUnionMemberTypes) == 0 {
+ return
+ }
+
+ if selectionSetInfo.hasFields {
+ for _, typeName := range filteredUnionMemberTypes {
+ *selectionRefs = append(*selectionRefs, r.createFragmentSelection(typeName, selectionSetInfo.fields))
+ }
+ }
+
+ for _, inlineFragmentInfo := range selectionSetInfo.inlineFragmentsOnObjects {
+ if !slices.Contains(filteredUnionMemberTypes, inlineFragmentInfo.typeName) {
+ continue
+ }
+
+ fragmentSelectionRef := r.operation.CopySelection(inlineFragmentInfo.selectionRef)
+ *selectionRefs = append(*selectionRefs, fragmentSelectionRef)
+ }
+
+ for _, inlineFragmentInfo := range selectionSetInfo.inlineFragmentsOnInterfaces {
+ typeNamesImplementingInterfaceInCurrentDS := inlineFragmentInfo.typeNamesImplementingInterfaceInCurrentDS
+ if len(typeNamesImplementingInterfaceInCurrentDS) == 0 {
+ typeNamesImplementingInterfaceInCurrentDS = inlineFragmentInfo.typeNamesImplementingInterface
+ }
+
+ r.flattenFragmentOnInterface(inlineFragmentInfo.selectionSetInfo, typeNamesImplementingInterfaceInCurrentDS, filteredUnionMemberTypes, selectionRefs)
+ }
+
+ for _, inlineFragmentInfo := range selectionSetInfo.inlineFragmentsOnUnions {
+ nestedUnionMemberTypeNamesInCurrentDS := inlineFragmentInfo.unionMemberTypeNamesInCurrentDS
+ if len(nestedUnionMemberTypeNamesInCurrentDS) == 0 {
+ nestedUnionMemberTypeNamesInCurrentDS = inlineFragmentInfo.unionMemberTypeNames
+ }
+
+ r.flattenFragmentOnUnion(inlineFragmentInfo.selectionSetInfo, nestedUnionMemberTypeNamesInCurrentDS, filteredUnionMemberTypes, selectionRefs)
}
}
diff --git a/tmp/agent-patch-flux-pr-1184.1-of-1.2026-02-28__13-38-10__gpt-5-3-codex/app/v2/pkg/engine/plan/abstract_selection_rewriter_helpers.go b/app/v2/pkg/engine/plan/abstract_selection_rewriter_helpers.go
index 17a53e1..1c191c0 100644
--- a/tmp/agent-patch-flux-pr-1184.1-of-1.2026-02-28__13-38-10__gpt-5-3-codex/app/v2/pkg/engine/plan/abstract_selection_rewriter_helpers.go
+++ b/app/v2/pkg/engine/plan/abstract_selection_rewriter_helpers.go
@@ -39,13 +39,13 @@ func (r *fieldSelectionRewriter) entitiesImplementingInterface(typesImplementing
return nil
}
- for _, typeName := range typesImplementingInterface {
- if slices.Contains(entityNames, typeName) {
+ for _, typeName := range entityNames {
+ if slices.Contains(typesImplementingInterface, typeName) {
out = append(out, typeName) //nolint:staticcheck
}
}
- return entityNames
+ return out
}
func (r *fieldSelectionRewriter) entityNamesWithoutFragments(inlineFragments []inlineFragmentSelection, entityNames []string) []string {
@@ -168,7 +168,21 @@ func (r *fieldSelectionRewriter) interfaceFragmentsRequiresCleanup(inlineFragmen
return false
}
-func (r *fieldSelectionRewriter) objectFragmentNeedCleanup(inlineFragment inlineFragmentSelection) bool {
+func (r *fieldSelectionRewriter) unionFragmentsRequiresCleanup(inlineFragments []inlineFragmentSelectionOnUnion, parentSelectionValidTypes []string) bool {
+ for _, fragment := range inlineFragments {
+ if r.unionFragmentNeedCleanup(fragment, parentSelectionValidTypes) {
+ return true
+ }
+ }
+
+ return false
+}
+
+func (r *fieldSelectionRewriter) objectFragmentNeedCleanup(inlineFragment inlineFragmentSelection, parentSelectionValidTypes []string) bool {
+ if !slices.Contains(parentSelectionValidTypes, inlineFragment.typeName) {
+ return true
+ }
+
if !r.hasTypeOnDataSource(inlineFragment.typeName) {
return true
}
@@ -179,6 +193,66 @@ func (r *fieldSelectionRewriter) objectFragmentNeedCleanup(inlineFragment inline
}
}
+ for _, fragmentOnUnion := range inlineFragment.selectionSetInfo.inlineFragmentsOnUnions {
+ if r.unionFragmentNeedCleanup(fragmentOnUnion, []string{inlineFragment.typeName}) {
+ return true
+ }
+ }
+
+ return false
+}
+
+func (r *fieldSelectionRewriter) unionFragmentNeedCleanup(inlineFragment inlineFragmentSelectionOnUnion, parentSelectionValidTypes []string) bool {
+ if !r.hasTypeOnDataSource(inlineFragment.typeName) {
+ return true
+ }
+
+ unionMemberTypeNamesInCurrentDS := inlineFragment.unionMemberTypeNamesInCurrentDS
+ if len(unionMemberTypeNamesInCurrentDS) == 0 {
+ unionMemberTypeNamesInCurrentDS = inlineFragment.unionMemberTypeNames
+ }
+
+ filteredUnionMemberTypeNames := make([]string, 0, len(unionMemberTypeNamesInCurrentDS))
+ for _, typeName := range unionMemberTypeNamesInCurrentDS {
+ if slices.Contains(parentSelectionValidTypes, typeName) {
+ filteredUnionMemberTypeNames = append(filteredUnionMemberTypeNames, typeName)
+ }
+ }
+
+ if len(filteredUnionMemberTypeNames) == 0 {
+ return true
+ }
+
+ for _, fragmentOnObject := range inlineFragment.selectionSetInfo.inlineFragmentsOnObjects {
+ if r.objectFragmentNeedCleanup(fragmentOnObject, filteredUnionMemberTypeNames) {
+ return true
+ }
+ }
+
+ for _, fragmentOnInterface := range inlineFragment.selectionSetInfo.inlineFragmentsOnInterfaces {
+ if r.interfaceFragmentNeedCleanup(fragmentOnInterface, filteredUnionMemberTypeNames) {
+ return true
+ }
+ }
+
+ for _, fragmentOnUnion := range inlineFragment.selectionSetInfo.inlineFragmentsOnUnions {
+ if r.unionFragmentNeedCleanup(fragmentOnUnion, filteredUnionMemberTypeNames) {
+ return true
+ }
+ }
+
+ if inlineFragment.selectionSetInfo.hasFields {
+ for _, typeName := range filteredUnionMemberTypeNames {
+ if !r.typeHasAllFieldLocal(typeName, inlineFragment.selectionSetInfo.fields) {
+ return true
+ }
+
+ if r.hasRequiresConfigurationForField(typeName, inlineFragment.selectionSetInfo.fields) {
+ return true
+ }
+ }
+ }
+
return false
}
@@ -202,11 +276,7 @@ func (r *fieldSelectionRewriter) interfaceFragmentNeedCleanup(inlineFragment inl
// check each fragment for the presence of other interface fragments
if inlineFragment.selectionSetInfo.hasInlineFragmentsOnObjects {
for _, fragmentOnObject := range inlineFragment.selectionSetInfo.inlineFragmentsOnObjects {
- if !slices.Contains(parentSelectionValidTypes, fragmentOnObject.typeName) {
- return true
- }
-
- if r.objectFragmentNeedCleanup(fragmentOnObject) {
+ if r.objectFragmentNeedCleanup(fragmentOnObject, parentSelectionValidTypes) {
return true
}
}
@@ -222,6 +292,14 @@ func (r *fieldSelectionRewriter) interfaceFragmentNeedCleanup(inlineFragment inl
}
}
+ if inlineFragment.selectionSetInfo.hasInlineFragmentsOnUnions {
+ for _, fragmentOnUnion := range inlineFragment.selectionSetInfo.inlineFragmentsOnUnions {
+ if r.unionFragmentNeedCleanup(fragmentOnUnion, parentSelectionValidTypes) {
+ return true
+ }
+ }
+ }
+
if inlineFragment.selectionSetInfo.hasFields {
// NOTE: maybe we need to filter this typenames by parentSelectionValidTypes?
for _, typeName := range inlineFragment.typeNamesImplementingInterfaceInCurrentDS {
diff --git a/tmp/agent-patch-flux-pr-1184.1-of-1.2026-02-28__13-38-10__gpt-5-3-codex/app/v2/pkg/engine/plan/abstract_selection_rewriter_info.go b/app/v2/pkg/engine/plan/abstract_selection_rewriter_info.go
index 950ccb0..ed92045 100644
--- a/tmp/agent-patch-flux-pr-1184.1-of-1.2026-02-28__13-38-10__gpt-5-3-codex/app/v2/pkg/engine/plan/abstract_selection_rewriter_info.go
+++ b/app/v2/pkg/engine/plan/abstract_selection_rewriter_info.go
@@ -146,6 +146,7 @@ func (r *fieldSelectionRewriter) collectInlineFragmentInformation(
if hasUpstreamNode {
if upstreamNode.Kind == ast.NodeKindInterfaceTypeDefinition {
typeNamesImplementingInterface, _ := r.upstreamDefinition.InterfaceTypeDefinitionImplementedByObjectWithNames(upstreamNode.Ref)
+ sort.Strings(typeNamesImplementingInterface)
entityNames, _ := r.datasourceHasEntitiesWithName(typeNamesImplementingInterface)
inlineFragmentSelectionOnInterface.typeNamesImplementingInterfaceInCurrentDS = typeNamesImplementingInterface
@@ -159,8 +160,9 @@ func (r *fieldSelectionRewriter) collectInlineFragmentInformation(
sort.Strings(unionMemberTypeNames)
inlineFragmentSelectionOnUnion := inlineFragmentSelectionOnUnion{
- inlineFragmentSelection: inlineFragmentSelection,
- unionMemberTypeNames: unionMemberTypeNames,
+ inlineFragmentSelection: inlineFragmentSelection,
+ unionMemberTypeNames: unionMemberTypeNames,
+ unionMemberTypeNamesInCurrentDS: unionMemberTypeNames,
}
// NOTE: We are getting type names of union members from the current SUBGRAPH definion
@@ -169,9 +171,10 @@ func (r *fieldSelectionRewriter) collectInlineFragmentInformation(
if hasUpstreamNode {
if upstreamNode.Kind == ast.NodeKindUnionTypeDefinition {
unionMemberTypeNamesFromCurrentDS, _ := r.upstreamDefinition.UnionTypeDefinitionMemberTypeNames(upstreamNode.Ref)
+ sort.Strings(unionMemberTypeNamesFromCurrentDS)
entityNames, _ := r.datasourceHasEntitiesWithName(unionMemberTypeNamesFromCurrentDS)
- inlineFragmentSelectionOnUnion.unionMemberTypeNames = unionMemberTypeNamesFromCurrentDS
+ inlineFragmentSelectionOnUnion.unionMemberTypeNamesInCurrentDS = unionMemberTypeNamesFromCurrentDS
inlineFragmentSelectionOnUnion.unionMemberEntityNames = entityNames
}
}
diff --git a/tmp/agent-patch-flux-pr-1184.1-of-1.2026-02-28__13-38-10__gpt-5-3-codex/app/v2/pkg/engine/plan/abstract_selection_rewriter_test.go b/app/v2/pkg/engine/plan/abstract_selection_rewriter_test.go
index c58465a..962bb01 100644
--- a/tmp/agent-patch-flux-pr-1184.1-of-1.2026-02-28__13-38-10__gpt-5-3-codex/app/v2/pkg/engine/plan/abstract_selection_rewriter_test.go
+++ b/app/v2/pkg/engine/plan/abstract_selection_rewriter_test.go
@@ -2437,6 +2437,199 @@ func TestInterfaceSelectionRewriter_RewriteOperation(t *testing.T) {
}`,
shouldRewrite: true,
},
+ {
+ name: "interface rewrite keeps nested union fragments and filters impossible nested concrete types",
+ definition: `
+ interface HasName {
+ name: String!
+ }
+
+ type User implements HasName {
+ id: ID!
+ name: String!
+ isUser: Boolean!
+ }
+
+ type Admin implements HasName {
+ id: ID!
+ name: String!
+ }
+
+ type Moderator {
+ id: ID!
+ name: String!
+ isModerator: Boolean!
+ }
+
+ union Account = User | Admin | Moderator
+
+ type Query {
+ iface: HasName!
+ }`,
+ upstreamDefinition: `
+ interface HasName {
+ name: String!
+ }
+
+ type User implements HasName {
+ id: ID!
+ name: String!
+ isUser: Boolean!
+ }
+
+ type Admin implements HasName {
+ id: ID!
+ }
+
+ type Moderator {
+ id: ID!
+ name: String!
+ isModerator: Boolean!
+ }
+
+ union Account = User | Admin | Moderator
+
+ type Query {
+ iface: HasName!
+ }`,
+ dsConfiguration: dsb().
+ RootNode("Query", "iface").
+ RootNode("User", "id", "name", "isUser").
+ RootNode("Admin", "id").
+ RootNode("Moderator", "id", "name", "isModerator").
+ RootNode("HasName", "name").
+ RootNode("Account", "__typename").
+ KeysMetadata(FederationFieldConfigurations{
+ {
+ TypeName: "User",
+ SelectionSet: "id",
+ },
+ {
+ TypeName: "Admin",
+ SelectionSet: "id",
+ },
+ {
+ TypeName: "Moderator",
+ SelectionSet: "id",
+ },
+ }).
+ DS(),
+ operation: `
+ query {
+ iface {
+ name
+ ... on Account {
+ ... on User {
+ isUser
+ }
+ ... on Moderator {
+ isModerator
+ }
+ }
+ }
+ }`,
+ expectedOperation: `
+ query {
+ iface {
+ ... on Admin {
+ name
+ }
+ ... on User {
+ name
+ isUser
+ }
+ }
+ }`,
+ shouldRewrite: true,
+ },
+ {
+ name: "union rewrite keeps nested union fragments inside interface fragments",
+ fieldName: "returnsUnion",
+ definition: `
+ interface HasName {
+ name: String!
+ }
+
+ type User implements HasName {
+ id: ID!
+ name: String!
+ isUser: Boolean!
+ }
+
+ type Admin implements HasName {
+ id: ID!
+ name: String!
+ }
+
+ union Account = User | Admin
+
+ type Query {
+ returnsUnion: Account!
+ }`,
+ upstreamDefinition: `
+ interface HasName {
+ name: String!
+ }
+
+ type User implements HasName {
+ id: ID!
+ name: String!
+ isUser: Boolean!
+ }
+
+ type Admin implements HasName {
+ id: ID!
+ }
+
+ union Account = User | Admin
+
+ type Query {
+ returnsUnion: Account!
+ }`,
+ dsConfiguration: dsb().
+ RootNode("Query", "returnsUnion").
+ RootNode("User", "id", "name", "isUser").
+ RootNode("Admin", "id").
+ RootNode("HasName", "name").
+ RootNode("Account", "__typename").
+ KeysMetadata(FederationFieldConfigurations{
+ {
+ TypeName: "User",
+ SelectionSet: "id",
+ },
+ {
+ TypeName: "Admin",
+ SelectionSet: "id",
+ },
+ }).
+ DS(),
+ operation: `
+ query {
+ returnsUnion {
+ ... on HasName {
+ name
+ ... on Account {
+ ... on User {
+ isUser
+ }
+ }
+ }
+ }
+ }`,
+ expectedOperation: `
+ query {
+ returnsUnion {
+ ... on Admin {
+ name
+ }
+ ... on User {
+ name
+ isUser
+ }
+ }
+ }`,
+ shouldRewrite: true,
+ },
{
name: "everything is local, nested interface selections with typename, first interface is matching field return type",
definition: `