agent_patch
runs/2026-02-28__13-38-10__gpt-5-1-codex-mini/flux-pr-1184/flux-pr-1184.1-of-1.2026-02-28__13-38-10__gpt-5-1-codex-mini/agent-logs/agent.patch
13855 bytes
diff --git a/tmp/agent-patch-flux-pr-1184.1-of-1.2026-02-28__13-38-10__gpt-5-1-codex-mini/app/v2/pkg/engine/plan/abstract_selection_rewriter.go b/app/v2/pkg/engine/plan/abstract_selection_rewriter.go
index 3484ce9..c90e48a 100644
--- a/tmp/agent-patch-flux-pr-1184.1-of-1.2026-02-28__13-38-10__gpt-5-1-codex-mini/app/v2/pkg/engine/plan/abstract_selection_rewriter.go
+++ b/app/v2/pkg/engine/plan/abstract_selection_rewriter.go
@@ -168,6 +168,10 @@ func (r *fieldSelectionRewriter) unionFieldSelectionNeedsRewrite(selectionSetInf
return true
}
+ if selectionSetInfo.hasInlineFragmentsOnUnions {
+ return true
+ }
+
// when we do not have fragments on interfaces, but only on objects - we do not need to rewrite
if !selectionSetInfo.hasInlineFragmentsOnInterfaces {
return false
@@ -222,6 +226,10 @@ func (r *fieldSelectionRewriter) rewriteUnionSelection(fieldRef int, fieldInfo s
r.flattenFragmentOnInterface(inlineFragmentOnInterface.selectionSetInfo, inlineFragmentOnInterface.typeNamesImplementingInterface, unionTypeNames, &newSelectionRefs)
}
+ for _, inlineFragmentOnUnion := range fieldInfo.inlineFragmentsOnUnions {
+ r.flattenFragmentOnUnion(inlineFragmentOnUnion, unionTypeNames, 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
filteredObjectFragments, _ := r.filterFragmentsByTypeNames(fieldInfo.inlineFragmentsOnObjects, unionTypeNames)
@@ -304,6 +312,10 @@ func (r *fieldSelectionRewriter) processInterfaceSelection(fieldRef int, interfa
}
func (r *fieldSelectionRewriter) interfaceFieldSelectionNeedsRewrite(selectionSetInfo selectionSetInfo, interfaceTypeNames []string, entityNames []string) (needRewrite bool) {
+ if selectionSetInfo.hasInlineFragmentsOnUnions {
+ return true
+ }
+
// when we do not have fragments
if !selectionSetInfo.hasInlineFragmentsOnInterfaces && !selectionSetInfo.hasInlineFragmentsOnObjects {
// check that all types implementing the interface have a root node with the requested fields
@@ -440,6 +452,41 @@ func (r *fieldSelectionRewriter) flattenFragmentOnInterface(selectionSetInfo sel
r.flattenFragmentOnInterface(inlineFragmentInfo.selectionSetInfo, inlineFragmentInfo.typeNamesImplementingInterface, filteredImplementingTypes, selectionRefs)
}
+
+ for _, inlineFragmentInfo := range selectionSetInfo.inlineFragmentsOnUnions {
+ r.flattenFragmentOnUnion(inlineFragmentInfo, filteredImplementingTypes, allowedTypeNames, selectionRefs)
+ }
+}
+
+func (r *fieldSelectionRewriter) flattenFragmentOnUnion(inlineFragment inlineFragmentSelectionOnUnion, parentSelectionValidTypes []string, allowedTypeNames []string, selectionRefs *[]int) {
+ filteredUnionMemberTypes := r.allowedUnionMemberTypeNames(inlineFragment, parentSelectionValidTypes, allowedTypeNames)
+ if len(filteredUnionMemberTypes) == 0 {
+ return
+ }
+
+ if inlineFragment.selectionSetInfo.hasFields {
+ for _, typeName := range filteredUnionMemberTypes {
+ *selectionRefs = append(*selectionRefs, r.createFragmentSelection(typeName, inlineFragment.selectionSetInfo.fields))
+ }
+ }
+
+ for _, inlineFragmentInfo := range inlineFragment.selectionSetInfo.inlineFragmentsOnObjects {
+ if !slices.Contains(filteredUnionMemberTypes, inlineFragmentInfo.typeName) {
+ continue
+ }
+
+ fragmentSelectionRef := r.operation.CopySelection(inlineFragmentInfo.selectionRef)
+
+ *selectionRefs = append(*selectionRefs, fragmentSelectionRef)
+ }
+
+ for _, inlineFragmentInfo := range inlineFragment.selectionSetInfo.inlineFragmentsOnInterfaces {
+ r.flattenFragmentOnInterface(inlineFragmentInfo.selectionSetInfo, inlineFragmentInfo.typeNamesImplementingInterfaceInCurrentDS, filteredUnionMemberTypes, selectionRefs)
+ }
+
+ for _, inlineFragmentInfo := range inlineFragment.selectionSetInfo.inlineFragmentsOnUnions {
+ r.flattenFragmentOnUnion(inlineFragmentInfo, filteredUnionMemberTypes, filteredUnionMemberTypes, selectionRefs)
+ }
}
func (r *fieldSelectionRewriter) collectChangedRefs(fieldRef int, fieldRefsPaths map[int]string) (map[int][]int, error) {
diff --git a/tmp/agent-patch-flux-pr-1184.1-of-1.2026-02-28__13-38-10__gpt-5-1-codex-mini/app/v2/pkg/engine/plan/abstract_selection_rewriter_helpers.go b/app/v2/pkg/engine/plan/abstract_selection_rewriter_helpers.go
index 17a53e1..b65955f 100644
--- a/tmp/agent-patch-flux-pr-1184.1-of-1.2026-02-28__13-38-10__gpt-5-1-codex-mini/app/v2/pkg/engine/plan/abstract_selection_rewriter_helpers.go
+++ b/app/v2/pkg/engine/plan/abstract_selection_rewriter_helpers.go
@@ -45,7 +45,7 @@ func (r *fieldSelectionRewriter) entitiesImplementingInterface(typesImplementing
}
}
- return entityNames
+ return out
}
func (r *fieldSelectionRewriter) entityNamesWithoutFragments(inlineFragments []inlineFragmentSelection, entityNames []string) []string {
@@ -168,7 +168,11 @@ func (r *fieldSelectionRewriter) interfaceFragmentsRequiresCleanup(inlineFragmen
return false
}
-func (r *fieldSelectionRewriter) objectFragmentNeedCleanup(inlineFragment inlineFragmentSelection) bool {
+func (r *fieldSelectionRewriter) objectFragmentNeedCleanup(inlineFragment inlineFragmentSelection, parentSelectionValidTypes []string) bool {
+ if len(parentSelectionValidTypes) > 0 && !slices.Contains(parentSelectionValidTypes, inlineFragment.typeName) {
+ return true
+ }
+
if !r.hasTypeOnDataSource(inlineFragment.typeName) {
return true
}
@@ -179,6 +183,14 @@ func (r *fieldSelectionRewriter) objectFragmentNeedCleanup(inlineFragment inline
}
}
+ if inlineFragment.selectionSetInfo.hasInlineFragmentsOnUnions {
+ for _, fragmentOnUnion := range inlineFragment.selectionSetInfo.inlineFragmentsOnUnions {
+ if r.unionFragmentNeedCleanup(fragmentOnUnion, []string{inlineFragment.typeName}) {
+ return true
+ }
+ }
+ }
+
return false
}
@@ -206,7 +218,7 @@ func (r *fieldSelectionRewriter) interfaceFragmentNeedCleanup(inlineFragment inl
return true
}
- if r.objectFragmentNeedCleanup(fragmentOnObject) {
+ if r.objectFragmentNeedCleanup(fragmentOnObject, parentSelectionValidTypes) {
return true
}
}
@@ -222,6 +234,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 {
@@ -238,6 +258,85 @@ func (r *fieldSelectionRewriter) interfaceFragmentNeedCleanup(inlineFragment inl
return false
}
+func (r *fieldSelectionRewriter) unionFragmentNeedCleanup(inlineFragment inlineFragmentSelectionOnUnion, parentSelectionValidTypes []string) bool {
+ if !r.hasTypeOnDataSource(inlineFragment.typeName) {
+ return true
+ }
+
+ filteredUnionMembers := r.allowedUnionMemberTypeNames(inlineFragment, parentSelectionValidTypes, nil)
+ if len(filteredUnionMembers) == 0 {
+ return false
+ }
+
+ if inlineFragment.selectionSetInfo.hasFields {
+ for _, typeName := range filteredUnionMembers {
+ if !r.typeHasAllFieldLocal(typeName, inlineFragment.selectionSetInfo.fields) {
+ return true
+ }
+
+ if r.hasRequiresConfigurationForField(typeName, inlineFragment.selectionSetInfo.fields) {
+ return true
+ }
+ }
+ }
+
+ if inlineFragment.selectionSetInfo.hasInlineFragmentsOnObjects {
+ for _, fragmentOnObject := range inlineFragment.selectionSetInfo.inlineFragmentsOnObjects {
+ if !slices.Contains(filteredUnionMembers, fragmentOnObject.typeName) {
+ return true
+ }
+
+ if r.objectFragmentNeedCleanup(fragmentOnObject, filteredUnionMembers) {
+ return true
+ }
+ }
+ }
+
+ if inlineFragment.selectionSetInfo.hasInlineFragmentsOnInterfaces {
+ for _, fragmentOnInterface := range inlineFragment.selectionSetInfo.inlineFragmentsOnInterfaces {
+ if r.interfaceFragmentNeedCleanup(fragmentOnInterface, filteredUnionMembers) {
+ return true
+ }
+ }
+ }
+
+ if inlineFragment.selectionSetInfo.hasInlineFragmentsOnUnions {
+ for _, fragmentOnUnion := range inlineFragment.selectionSetInfo.inlineFragmentsOnUnions {
+ if r.unionFragmentNeedCleanup(fragmentOnUnion, filteredUnionMembers) {
+ return true
+ }
+ }
+ }
+
+ return false
+}
+
+func (r *fieldSelectionRewriter) allowedUnionMemberTypeNames(inlineFragment inlineFragmentSelectionOnUnion, parentSelectionValidTypes []string, allowedTypeNames []string) []string {
+ unionMemberTypeNames := inlineFragment.unionMemberTypeNames
+ if len(inlineFragment.unionMemberTypeNamesInCurrentDS) > 0 {
+ unionMemberTypeNames = inlineFragment.unionMemberTypeNamesInCurrentDS
+ }
+
+ if len(unionMemberTypeNames) == 0 {
+ return nil
+ }
+
+ filtered := make([]string, 0, len(unionMemberTypeNames))
+ for _, typeName := range unionMemberTypeNames {
+ if len(parentSelectionValidTypes) > 0 && !slices.Contains(parentSelectionValidTypes, typeName) {
+ continue
+ }
+
+ if len(allowedTypeNames) > 0 && !slices.Contains(allowedTypeNames, typeName) {
+ continue
+ }
+
+ filtered = append(filtered, typeName)
+ }
+
+ return filtered
+}
+
func (r *fieldSelectionRewriter) typeHasAllFieldLocal(typeName string, fields []fieldSelection) bool {
for _, field := range fields {
if field.fieldName == typeNameField {
diff --git a/tmp/agent-patch-flux-pr-1184.1-of-1.2026-02-28__13-38-10__gpt-5-1-codex-mini/app/v2/pkg/engine/plan/abstract_selection_rewriter_info.go b/app/v2/pkg/engine/plan/abstract_selection_rewriter_info.go
index 950ccb0..2085abf 100644
--- a/tmp/agent-patch-flux-pr-1184.1-of-1.2026-02-28__13-38-10__gpt-5-1-codex-mini/app/v2/pkg/engine/plan/abstract_selection_rewriter_info.go
+++ b/app/v2/pkg/engine/plan/abstract_selection_rewriter_info.go
@@ -171,7 +171,7 @@ func (r *fieldSelectionRewriter) collectInlineFragmentInformation(
unionMemberTypeNamesFromCurrentDS, _ := r.upstreamDefinition.UnionTypeDefinitionMemberTypeNames(upstreamNode.Ref)
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-1-codex-mini/app/v2/pkg/engine/plan/abstract_selection_rewriter_test.go b/app/v2/pkg/engine/plan/abstract_selection_rewriter_test.go
index c58465a..418ac0c 100644
--- a/tmp/agent-patch-flux-pr-1184.1-of-1.2026-02-28__13-38-10__gpt-5-1-codex-mini/app/v2/pkg/engine/plan/abstract_selection_rewriter_test.go
+++ b/app/v2/pkg/engine/plan/abstract_selection_rewriter_test.go
@@ -1265,6 +1265,162 @@ func TestInterfaceSelectionRewriter_RewriteOperation(t *testing.T) {
}`,
shouldRewrite: false,
},
+ {
+ name: "nested union fragment inside interface field gets flattened",
+ definition: `
+ interface Node {
+ name: String!
+ }
+
+ type User implements Node {
+ name: String!
+ }
+
+ type Admin implements Node {
+ name: String!
+ }
+
+ union Account = User | Admin
+
+ type Query {
+ iface: Node!
+ }
+ `,
+ upstreamDefinition: `
+ interface Node {
+ name: String!
+ }
+
+ type User implements Node {
+ name: String!
+ }
+
+ type Admin implements Node {
+ name: String!
+ }
+
+ union Account = User | Admin
+
+ type Query {
+ iface: Node!
+ }
+ `,
+ dsConfiguration: dsb().
+ RootNode("Query", "iface").
+ RootNode("User", "name").
+ RootNode("Admin", "name").
+ KeysMetadata(FederationFieldConfigurations{
+ {
+ TypeName: "User",
+ SelectionSet: "name",
+ },
+ {
+ TypeName: "Admin",
+ SelectionSet: "name",
+ },
+ }).
+ DS(),
+ operation: `
+ query {
+ iface {
+ ... on Node {
+ ... on Account {
+ name
+ }
+ }
+ }
+ }
+ `,
+ expectedOperation: `
+ query {
+ iface {
+ ... on User {
+ name
+ }
+ ... on Admin {
+ name
+ }
+ }
+ }
+ `,
+ shouldRewrite: true,
+ },
+ {
+ name: "nested union fragment inside union field gets flattened",
+ fieldName: "accounts",
+ definition: `
+ type User {
+ name: String!
+ }
+
+ type Admin {
+ name: String!
+ }
+
+ union Account = User | Admin
+
+ type Query {
+ accounts: [Account!]!
+ }
+ `,
+ upstreamDefinition: `
+ type User {
+ name: String!
+ }
+
+ type Admin {
+ name: String!
+ }
+
+ union Account = User | Admin
+
+ type Query {
+ accounts: [Account!]!
+ }
+ `,
+ dsConfiguration: dsb().
+ RootNode("Query", "accounts").
+ RootNode("User", "name").
+ RootNode("Admin", "name").
+ KeysMetadata(FederationFieldConfigurations{
+ {
+ TypeName: "User",
+ SelectionSet: "name",
+ },
+ {
+ TypeName: "Admin",
+ SelectionSet: "name",
+ },
+ }).
+ DS(),
+ operation: `
+ query {
+ accounts {
+ ... on Account {
+ ... on User {
+ name
+ }
+ ... on Admin {
+ name
+ }
+ }
+ }
+ }
+ `,
+ expectedOperation: `
+ query {
+ accounts {
+ ... on User {
+ name
+ }
+ ... on Admin {
+ name
+ }
+ }
+ }
+ `,
+ shouldRewrite: true,
+ },
{
name: "field is a type",
definition: `