agent_patch
runs/2026-02-21__03-36-16__gpt-5-1-codex-mini/flux-pr-1240/flux-pr-1240.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/agent-logs/agent.patch
27348 bytes
diff --git a/tmp/agent-patch-flux-pr-1240.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/app/pkg/astnormalization/astnormalization.go b/app/pkg/astnormalization/astnormalization.go
index 7cb6e18..1454123 100644
--- a/tmp/agent-patch-flux-pr-1240.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/app/pkg/astnormalization/astnormalization.go
+++ b/app/pkg/astnormalization/astnormalization.go
@@ -175,7 +175,6 @@ func (o *OperationNormalizer) setupOperationWalkers() {
other := astvisitor.NewWalker(48)
removeSelfAliasing(&other)
mergeInlineFragments(&other)
- mergeFieldSelections(&other)
deduplicateFields(&other)
if o.options.removeFragmentDefinitions {
diff --git a/tmp/agent-patch-flux-pr-1240.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/app/pkg/astnormalization/field_selection_merging.go b/tmp/agent-patch-flux-pr-1240.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/app/pkg/astnormalization/field_selection_merging.go
deleted file mode 100644
index 9e035af..0000000
--- a/tmp/agent-patch-flux-pr-1240.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/app/pkg/astnormalization/field_selection_merging.go
+++ /dev/null
@@ -1,93 +0,0 @@
-package astnormalization
-
-import (
- "bytes"
-
- "github.com/wundergraph/graphql-go-tools/pkg/ast"
- "github.com/wundergraph/graphql-go-tools/pkg/astvisitor"
-)
-
-func mergeFieldSelections(walker *astvisitor.Walker) {
- visitor := fieldSelectionMergeVisitor{
- Walker: walker,
- }
- walker.RegisterEnterDocumentVisitor(&visitor)
- walker.RegisterEnterSelectionSetVisitor(&visitor)
-}
-
-type fieldSelectionMergeVisitor struct {
- *astvisitor.Walker
- operation *ast.Document
-}
-
-func (f *fieldSelectionMergeVisitor) EnterDocument(operation, definition *ast.Document) {
- f.operation = operation
-}
-
-func (f *fieldSelectionMergeVisitor) fieldsCanMerge(left, right int) bool {
- leftName := f.operation.FieldNameBytes(left)
- rightName := f.operation.FieldNameBytes(right)
- leftAlias := f.operation.FieldAliasBytes(left)
- rightAlias := f.operation.FieldAliasBytes(right)
-
- if !bytes.Equal(leftName, rightName) || !bytes.Equal(leftAlias, rightAlias) {
- return false
- }
-
- leftDirectives := f.operation.FieldDirectives(left)
- rightDirectives := f.operation.FieldDirectives(right)
-
- return f.operation.DirectiveSetsAreEqual(leftDirectives, rightDirectives)
-}
-
-func (f *fieldSelectionMergeVisitor) isFieldSelection(ref int) bool {
- return f.operation.Selections[ref].Kind == ast.SelectionKindField
-}
-
-func (f *fieldSelectionMergeVisitor) fieldsHaveSelections(left, right int) bool {
- return f.operation.Fields[left].HasSelections && f.operation.Fields[right].HasSelections
-}
-
-func (f *fieldSelectionMergeVisitor) removeSelection(set, i int) {
- f.operation.SelectionSets[set].SelectionRefs = append(f.operation.SelectionSets[set].SelectionRefs[:i], f.operation.SelectionSets[set].SelectionRefs[i+1:]...)
-}
-
-func (f *fieldSelectionMergeVisitor) mergeFields(left, right int) {
- leftSet := f.operation.Fields[left].SelectionSet
- rightSet := f.operation.Fields[right].SelectionSet
- f.operation.SelectionSets[leftSet].SelectionRefs = append(f.operation.SelectionSets[leftSet].SelectionRefs, f.operation.SelectionSets[rightSet].SelectionRefs...)
- f.operation.Fields[left].Directives.Refs = append(f.operation.Fields[left].Directives.Refs, f.operation.Fields[right].Directives.Refs...)
-}
-
-func (f *fieldSelectionMergeVisitor) EnterSelectionSet(ref int) {
-
- if len(f.operation.SelectionSets[ref].SelectionRefs) < 2 {
- return
- }
-
- for _, leftSelection := range f.operation.SelectionSets[ref].SelectionRefs {
- if !f.isFieldSelection(leftSelection) {
- continue
- }
- leftField := f.operation.Selections[leftSelection].Ref
- for i, rightSelection := range f.operation.SelectionSets[ref].SelectionRefs {
- if !f.isFieldSelection(rightSelection) {
- continue
- }
- if leftSelection == rightSelection {
- continue
- }
- rightField := f.operation.Selections[rightSelection].Ref
- if !f.fieldsHaveSelections(leftField, rightField) {
- continue
- }
- if !f.fieldsCanMerge(leftField, rightField) {
- continue
- }
- f.removeSelection(ref, i)
- f.mergeFields(leftField, rightField)
- f.RevisitNode()
- return
- }
- }
-}
diff --git a/tmp/agent-patch-flux-pr-1240.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/app/pkg/astnormalization/field_selection_merging_test.go b/tmp/agent-patch-flux-pr-1240.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/app/pkg/astnormalization/field_selection_merging_test.go
deleted file mode 100644
index fb4207b..0000000
--- a/tmp/agent-patch-flux-pr-1240.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/app/pkg/astnormalization/field_selection_merging_test.go
+++ /dev/null
@@ -1,82 +0,0 @@
-package astnormalization
-
-import "testing"
-
-func TestMergeFieldSelections(t *testing.T) {
- t.Run("depth 1", func(t *testing.T) {
- run(mergeFieldSelections, testDefinition, `
- query conflictingBecauseAlias {
- dog {
- extra { string }
- extra { noString: string }
- }
- }`, `
- query conflictingBecauseAlias {
- dog {
- extra {
- string
- noString: string
- }
- }
- }`)
- })
- t.Run("depth 2", func(t *testing.T) {
- run(mergeFieldSelections, testDefinition, `
- query conflictingBecauseAlias {
- dog {
- extra { string }
- extra { string: noString }
- }
- dog {
- extra { string }
- extra { string: noString }
- }
- }`, `
- query conflictingBecauseAlias {
- dog {
- extra {
- string
- string: noString
- string
- string: noString
- }
- }
- }`)
- })
- t.Run("aliased", func(t *testing.T) {
- t.Run("aliased", func(t *testing.T) {
- run(mergeFieldSelections, testDefinition, `
- query conflictingBecauseAlias {
- dog {
- x: extras { string }
- x: mustExtras { string }
- }
- }`, `
- query conflictingBecauseAlias {
- dog {
- x: extras { string }
- x: mustExtras { string }
- }
- }`)
- })
- })
- t.Run("fields with directives", func(t *testing.T) {
- run(mergeFieldSelections, testDefinition, `
- {
- field @skip(if: $foo) {
- subfieldA
- }
- field @skip(if: $bar) {
- subfieldB
- }
- }`, `
- {
- field @skip(if: $foo) {
- subfieldA
- }
- field @skip(if: $bar) {
- subfieldB
- }
- }`)
- })
-}
diff --git a/tmp/agent-patch-flux-pr-1240.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/app/pkg/astnormalization/inline_fragment_merging.go b/app/pkg/astnormalization/inline_fragment_merging.go
index ca7f0df..387ce46 100644
--- a/tmp/agent-patch-flux-pr-1240.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/app/pkg/astnormalization/inline_fragment_merging.go
+++ b/app/pkg/astnormalization/inline_fragment_merging.go
@@ -25,6 +25,59 @@ func (m *mergeInlineFragmentsVisitor) EnterDocument(operation, definition *ast.D
m.definition = definition
}
+func (m *mergeInlineFragmentsVisitor) fieldsCanMerge(left, right int) bool {
+ leftName := m.operation.FieldNameBytes(left)
+ rightName := m.operation.FieldNameBytes(right)
+ leftAlias := m.operation.FieldAliasBytes(left)
+ rightAlias := m.operation.FieldAliasBytes(right)
+
+ if !bytes.Equal(leftName, rightName) || !bytes.Equal(leftAlias, rightAlias) {
+ return false
+ }
+
+ leftDirectives := m.operation.FieldDirectives(left)
+ rightDirectives := m.operation.FieldDirectives(right)
+
+ return m.operation.DirectiveSetsAreEqual(leftDirectives, rightDirectives)
+}
+
+func (m *mergeInlineFragmentsVisitor) isFieldSelection(ref int) bool {
+ return m.operation.Selections[ref].Kind == ast.SelectionKindField
+}
+
+func (m *mergeInlineFragmentsVisitor) fieldsHaveSelections(left, right int) bool {
+ return m.operation.Fields[left].HasSelections && m.operation.Fields[right].HasSelections
+}
+
+func (m *mergeInlineFragmentsVisitor) directivesContain(refs []int, candidate int) bool {
+ for _, ref := range refs {
+ if m.operation.DirectivesAreEqual(ref, candidate) {
+ return true
+ }
+ }
+ return false
+}
+
+func (m *mergeInlineFragmentsVisitor) removeSelection(set, i int) {
+ m.operation.SelectionSets[set].SelectionRefs = append(m.operation.SelectionSets[set].SelectionRefs[:i], m.operation.SelectionSets[set].SelectionRefs[i+1:]...)
+}
+
+func (m *mergeInlineFragmentsVisitor) mergeFields(left, right int) {
+ leftSet := m.operation.Fields[left].SelectionSet
+ rightSet := m.operation.Fields[right].SelectionSet
+ m.operation.SelectionSets[leftSet].SelectionRefs = append(m.operation.SelectionSets[leftSet].SelectionRefs, m.operation.SelectionSets[rightSet].SelectionRefs...)
+ leftDirectives := m.operation.Fields[left].Directives.Refs
+ for _, directiveRef := range m.operation.Fields[right].Directives.Refs {
+ if !m.directivesContain(leftDirectives, directiveRef) {
+ leftDirectives = append(leftDirectives, directiveRef)
+ }
+ }
+ if len(leftDirectives) > 0 {
+ m.operation.Fields[left].HasDirectives = true
+ }
+ m.operation.Fields[left].Directives.Refs = leftDirectives
+}
+
func (m *mergeInlineFragmentsVisitor) couldInline(set, inlineFragment int) bool {
if m.operation.InlineFragmentHasDirectives(inlineFragment) {
return false
@@ -60,4 +113,34 @@ func (m *mergeInlineFragmentsVisitor) EnterSelectionSet(ref int) {
m.RevisitNode()
return
}
+
+ if len(m.operation.SelectionSets[ref].SelectionRefs) < 2 {
+ return
+ }
+
+ for _, leftSelection := range m.operation.SelectionSets[ref].SelectionRefs {
+ if !m.isFieldSelection(leftSelection) {
+ continue
+ }
+ leftField := m.operation.Selections[leftSelection].Ref
+ for i, rightSelection := range m.operation.SelectionSets[ref].SelectionRefs {
+ if !m.isFieldSelection(rightSelection) {
+ continue
+ }
+ if leftSelection == rightSelection {
+ continue
+ }
+ rightField := m.operation.Selections[rightSelection].Ref
+ if !m.fieldsHaveSelections(leftField, rightField) {
+ continue
+ }
+ if !m.fieldsCanMerge(leftField, rightField) {
+ continue
+ }
+ m.removeSelection(ref, i)
+ m.mergeFields(leftField, rightField)
+ m.RevisitNode()
+ return
+ }
+ }
}
diff --git a/tmp/agent-patch-flux-pr-1240.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/app/pkg/astnormalization/inline_fragment_merging_test.go b/app/pkg/astnormalization/inline_fragment_merging_test.go
index f292dd2..41defdb 100644
--- a/tmp/agent-patch-flux-pr-1240.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/app/pkg/astnormalization/inline_fragment_merging_test.go
+++ b/app/pkg/astnormalization/inline_fragment_merging_test.go
@@ -32,8 +32,10 @@ func TestResolveInlineFragments(t *testing.T) {
nickname
doubleNested
nestedDogName
- extra { string }
- extra { string: noString }
+ extra {
+ string
+ string: noString
+ }
}
}`)
})
@@ -54,3 +56,97 @@ func TestResolveInlineFragments(t *testing.T) {
}`)
})
}
+
+func TestMergeFieldSelections(t *testing.T) {
+ t.Run("depth 1", func(t *testing.T) {
+ run(mergeInlineFragments, testDefinition, `
+ query conflictingBecauseAlias {
+ dog {
+ extra { string }
+ extra { noString: string }
+ }
+ }`, `
+ query conflictingBecauseAlias {
+ dog {
+ extra {
+ string
+ noString: string
+ }
+ }
+ }`)
+ })
+ t.Run("depth 2", func(t *testing.T) {
+ run(mergeInlineFragments, testDefinition, `
+ query conflictingBecauseAlias {
+ dog {
+ extra { string }
+ extra { string: noString }
+ }
+ dog {
+ extra { string }
+ extra { string: noString }
+ }
+ }`, `
+ query conflictingBecauseAlias {
+ dog {
+ extra {
+ string
+ string: noString
+ string
+ string: noString
+ }
+ }
+ }`)
+ })
+ t.Run("aliased fields do not merge", func(t *testing.T) {
+ run(mergeInlineFragments, testDefinition, `
+ query conflictingBecauseAlias {
+ dog {
+ x: extras { string }
+ x: mustExtras { string }
+ }
+ }`, `
+ query conflictingBecauseAlias {
+ dog {
+ x: extras { string }
+ x: mustExtras { string }
+ }
+ }`)
+ })
+ t.Run("fields with directives should not merge when directives differ", func(t *testing.T) {
+ run(mergeInlineFragments, testDefinition, `
+ {
+ field @skip(if: $foo) {
+ subfieldA
+ }
+ field @skip(if: $bar) {
+ subfieldB
+ }
+ }`, `
+ {
+ field @skip(if: $foo) {
+ subfieldA
+ }
+ field @skip(if: $bar) {
+ subfieldB
+ }
+ }`)
+ })
+ t.Run("fields with identical directives merge", func(t *testing.T) {
+ run(mergeInlineFragments, testDefinition, `
+ {
+ field @skip(if: $foo) {
+ subfieldA
+ }
+ field @skip(if: $foo) {
+ subfieldB
+ }
+ }`, `
+ {
+ field @skip(if: $foo) {
+ subfieldA
+ subfieldB
+ }
+ }`)
+ })
+}
diff --git a/tmp/agent-patch-flux-pr-1240.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/app/v2/pkg/astnormalization/abstract_field_normalizer.go b/app/v2/pkg/astnormalization/abstract_field_normalizer.go
index 1684566..16c365a 100644
--- a/tmp/agent-patch-flux-pr-1240.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/app/v2/pkg/astnormalization/abstract_field_normalizer.go
+++ b/app/v2/pkg/astnormalization/abstract_field_normalizer.go
@@ -31,7 +31,6 @@ func NewAbstractFieldNormalizer(operation *ast.Document, definition *ast.Documen
mergeInlineFragmentSelections(&walker)
inlineSelectionsFromInlineFragments(&walker)
- mergeFieldSelections(&walker)
deduplicateFields(&walker)
return normalizer
diff --git a/tmp/agent-patch-flux-pr-1240.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/app/v2/pkg/astnormalization/astnormalization.go b/app/v2/pkg/astnormalization/astnormalization.go
index 60ff1f2..6a26867 100644
--- a/tmp/agent-patch-flux-pr-1240.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/app/v2/pkg/astnormalization/astnormalization.go
+++ b/app/v2/pkg/astnormalization/astnormalization.go
@@ -212,7 +212,6 @@ func (o *OperationNormalizer) setupOperationWalkers() {
directiveIncludeSkip(&directivesIncludeSkip)
cleanup := astvisitor.NewWalker(8)
- mergeFieldSelections(&cleanup)
deduplicateFields(&cleanup)
if o.options.removeUnusedVariables {
del := deleteUnusedVariables(&cleanup)
@@ -272,7 +271,7 @@ func (o *OperationNormalizer) setupOperationWalkers() {
}
o.operationWalkers = append(o.operationWalkers, walkerStage{
- name: "mergeFieldSelections, deduplicateFields, deleteUnusedVariables",
+ name: "deduplicateFields, deleteUnusedVariables",
walker: &cleanup,
})
diff --git a/tmp/agent-patch-flux-pr-1240.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/app/v2/pkg/astnormalization/field_selection_merging.go b/tmp/agent-patch-flux-pr-1240.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/app/v2/pkg/astnormalization/field_selection_merging.go
deleted file mode 100644
index b0592db..0000000
--- a/tmp/agent-patch-flux-pr-1240.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/app/v2/pkg/astnormalization/field_selection_merging.go
+++ /dev/null
@@ -1,95 +0,0 @@
-package astnormalization
-
-import (
- "bytes"
-
- "github.com/wundergraph/graphql-go-tools/v2/pkg/ast"
- "github.com/wundergraph/graphql-go-tools/v2/pkg/astvisitor"
-)
-
-func mergeFieldSelections(walker *astvisitor.Walker) {
- visitor := fieldSelectionMergeVisitor{
- Walker: walker,
- }
- walker.RegisterEnterDocumentVisitor(&visitor)
- walker.RegisterEnterSelectionSetVisitor(&visitor)
-}
-
-type fieldSelectionMergeVisitor struct {
- *astvisitor.Walker
- operation *ast.Document
-}
-
-func (f *fieldSelectionMergeVisitor) EnterDocument(operation, definition *ast.Document) {
- f.operation = operation
-}
-
-func (f *fieldSelectionMergeVisitor) fieldsCanMerge(left, right int) bool {
- leftName := f.operation.FieldNameBytes(left)
- rightName := f.operation.FieldNameBytes(right)
- leftAlias := f.operation.FieldAliasBytes(left)
- rightAlias := f.operation.FieldAliasBytes(right)
-
- if !bytes.Equal(leftName, rightName) || !bytes.Equal(leftAlias, rightAlias) {
- return false
- }
-
- leftDirectives := f.operation.FieldDirectives(left)
- rightDirectives := f.operation.FieldDirectives(right)
-
- return f.operation.DirectiveSetsAreEqual(leftDirectives, rightDirectives)
-}
-
-func (f *fieldSelectionMergeVisitor) fieldsHaveSelections(left, right int) bool {
- return f.operation.Fields[left].HasSelections && f.operation.Fields[right].HasSelections
-}
-
-func (f *fieldSelectionMergeVisitor) mergeFields(left, right int) (ok bool) {
- var leftSet, rightSet int
- leftSet, ok = f.operation.FieldSelectionSet(left)
- if !ok {
- return false
- }
- rightSet, ok = f.operation.FieldSelectionSet(right)
- if !ok {
- return false
- }
-
- f.operation.AppendSelectionSet(leftSet, rightSet)
- return true
-}
-
-func (f *fieldSelectionMergeVisitor) EnterSelectionSet(ref int) {
-
- if len(f.operation.SelectionSets[ref].SelectionRefs) < 2 {
- return
- }
-
- for _, leftSelection := range f.operation.SelectionSets[ref].SelectionRefs {
- if !f.operation.SelectionIsFieldSelection(leftSelection) {
- continue
- }
- leftField := f.operation.Selections[leftSelection].Ref
- for i, rightSelection := range f.operation.SelectionSets[ref].SelectionRefs {
- if !f.operation.SelectionIsFieldSelection(rightSelection) {
- continue
- }
- if leftSelection == rightSelection {
- continue
- }
- rightField := f.operation.Selections[rightSelection].Ref
- if !f.fieldsHaveSelections(leftField, rightField) {
- continue
- }
- if !f.fieldsCanMerge(leftField, rightField) {
- continue
- }
-
- if f.mergeFields(leftField, rightField) {
- f.operation.RemoveFromSelectionSet(ref, i)
- f.RevisitNode()
- }
- return
- }
- }
-}
diff --git a/tmp/agent-patch-flux-pr-1240.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/app/v2/pkg/astnormalization/field_selection_merging_test.go b/tmp/agent-patch-flux-pr-1240.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/app/v2/pkg/astnormalization/field_selection_merging_test.go
deleted file mode 100644
index 91a1326..0000000
--- a/tmp/agent-patch-flux-pr-1240.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/app/v2/pkg/astnormalization/field_selection_merging_test.go
+++ /dev/null
@@ -1,100 +0,0 @@
-package astnormalization
-
-import "testing"
-
-func TestMergeFieldSelections(t *testing.T) {
- t.Run("depth 1", func(t *testing.T) {
- run(t, mergeFieldSelections, testDefinition, `
- query conflictingBecauseAlias {
- dog {
- extra { string }
- extra { noString: string }
- }
- }`, `
- query conflictingBecauseAlias {
- dog {
- extra {
- string
- noString: string
- }
- }
- }`)
- })
- t.Run("depth 2", func(t *testing.T) {
- run(t, mergeFieldSelections, testDefinition, `
- query conflictingBecauseAlias {
- dog {
- extra { string }
- extra { string: noString }
- }
- dog {
- extra { string }
- extra { string: noString }
- }
- }`, `
- query conflictingBecauseAlias {
- dog {
- extra {
- string
- string: noString
- string
- string: noString
- }
- }
- }`)
- })
- t.Run("aliased", func(t *testing.T) {
- t.Run("aliased", func(t *testing.T) {
- run(t, mergeFieldSelections, testDefinition, `
- query conflictingBecauseAlias {
- dog {
- x: extras { string }
- x: mustExtras { string }
- }
- }`, `
- query conflictingBecauseAlias {
- dog {
- x: extras { string }
- x: mustExtras { string }
- }
- }`)
- })
- })
- t.Run("fields with directives", func(t *testing.T) {
- run(t, mergeFieldSelections, testDefinition, `
- {
- field @skip(if: $foo) {
- subfieldA
- }
- field @skip(if: $bar) {
- subfieldB
- }
- }`, `
- {
- field @skip(if: $foo) {
- subfieldA
- }
- field @skip(if: $bar) {
- subfieldB
- }
- }`)
- })
-
- t.Run("fields with the same directives", func(t *testing.T) {
- run(t, mergeFieldSelections, testDefinition, `
- {
- field @skip(if: $foo) {
- subfieldA
- }
- field @skip(if: $foo) {
- subfieldB
- }
- }`, `
- {
- field @skip(if: $foo) {
- subfieldA
- subfieldB
- }
- }`)
- })
-}
diff --git a/tmp/agent-patch-flux-pr-1240.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/app/v2/pkg/astnormalization/inline_fragment_selection_merging.go b/app/v2/pkg/astnormalization/inline_fragment_selection_merging.go
index 39c94ff..76eb211 100644
--- a/tmp/agent-patch-flux-pr-1240.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/app/v2/pkg/astnormalization/inline_fragment_selection_merging.go
+++ b/app/v2/pkg/astnormalization/inline_fragment_selection_merging.go
@@ -24,6 +24,64 @@ func (f *inlineFragmentSelectionMergeVisitor) EnterDocument(operation, definitio
f.operation = operation
}
+func (f *inlineFragmentSelectionMergeVisitor) fieldsCanMerge(left, right int) bool {
+ leftName := f.operation.FieldNameBytes(left)
+ rightName := f.operation.FieldNameBytes(right)
+ leftAlias := f.operation.FieldAliasBytes(left)
+ rightAlias := f.operation.FieldAliasBytes(right)
+
+ if !bytes.Equal(leftName, rightName) || !bytes.Equal(leftAlias, rightAlias) {
+ return false
+ }
+
+ leftDirectives := f.operation.FieldDirectives(left)
+ rightDirectives := f.operation.FieldDirectives(right)
+
+ return f.operation.DirectiveSetsAreEqual(leftDirectives, rightDirectives)
+}
+
+func (f *inlineFragmentSelectionMergeVisitor) isFieldSelection(ref int) bool {
+ return f.operation.Selections[ref].Kind == ast.SelectionKindField
+}
+
+func (f *inlineFragmentSelectionMergeVisitor) fieldsHaveSelections(left, right int) bool {
+ return f.operation.Fields[left].HasSelections && f.operation.Fields[right].HasSelections
+}
+
+func (f *inlineFragmentSelectionMergeVisitor) directivesContain(refs []int, candidate int) bool {
+ for _, ref := range refs {
+ if f.operation.DirectivesAreEqual(ref, candidate) {
+ return true
+ }
+ }
+ return false
+}
+
+func (f *inlineFragmentSelectionMergeVisitor) mergeFields(left, right int) (ok bool) {
+ var leftSet, rightSet int
+ leftSet, ok = f.operation.FieldSelectionSet(left)
+ if !ok {
+ return false
+ }
+ rightSet, ok = f.operation.FieldSelectionSet(right)
+ if !ok {
+ return false
+ }
+
+ f.operation.AppendSelectionSet(leftSet, rightSet)
+ leftDirectives := f.operation.Fields[left].Directives.Refs
+ for _, directiveRef := range f.operation.Fields[right].Directives.Refs {
+ if !f.directivesContain(leftDirectives, directiveRef) {
+ leftDirectives = append(leftDirectives, directiveRef)
+ }
+ }
+ if len(leftDirectives) > 0 {
+ f.operation.Fields[left].HasDirectives = true
+ }
+ f.operation.Fields[left].Directives.Refs = leftDirectives
+ return true
+}
+
func (f *inlineFragmentSelectionMergeVisitor) fragmentsCanBeMerged(left, right int) bool {
leftName := f.operation.InlineFragmentTypeConditionName(left)
rightName := f.operation.InlineFragmentTypeConditionName(right)
@@ -55,10 +113,6 @@ func (f *inlineFragmentSelectionMergeVisitor) mergeInlineFragments(left, right i
}
func (f *inlineFragmentSelectionMergeVisitor) EnterSelectionSet(ref int) {
- if len(f.operation.SelectionSets[ref].SelectionRefs) < 2 {
- return
- }
-
for _, leftSelection := range f.operation.SelectionSets[ref].SelectionRefs {
if !f.operation.SelectionIsInlineFragmentSelection(leftSelection) {
continue
@@ -83,4 +137,35 @@ func (f *inlineFragmentSelectionMergeVisitor) EnterSelectionSet(ref int) {
return
}
}
+
+ if len(f.operation.SelectionSets[ref].SelectionRefs) < 2 {
+ return
+ }
+
+ for _, leftSelection := range f.operation.SelectionSets[ref].SelectionRefs {
+ if !f.isFieldSelection(leftSelection) {
+ continue
+ }
+ leftField := f.operation.Selections[leftSelection].Ref
+ for i, rightSelection := range f.operation.SelectionSets[ref].SelectionRefs {
+ if !f.isFieldSelection(rightSelection) {
+ continue
+ }
+ if leftSelection == rightSelection {
+ continue
+ }
+ rightField := f.operation.Selections[rightSelection].Ref
+ if !f.fieldsHaveSelections(leftField, rightField) {
+ continue
+ }
+ if !f.fieldsCanMerge(leftField, rightField) {
+ continue
+ }
+ if f.mergeFields(leftField, rightField) {
+ f.operation.RemoveFromSelectionSet(ref, i)
+ f.RevisitNode()
+ }
+ return
+ }
+ }
}
diff --git a/tmp/agent-patch-flux-pr-1240.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/app/v2/pkg/astnormalization/inline_fragment_selection_merging_test.go b/app/v2/pkg/astnormalization/inline_fragment_selection_merging_test.go
index 8433719..0f0a008 100644
--- a/tmp/agent-patch-flux-pr-1240.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/app/v2/pkg/astnormalization/inline_fragment_selection_merging_test.go
+++ b/app/v2/pkg/astnormalization/inline_fragment_selection_merging_test.go
@@ -122,3 +122,97 @@ func TestMergeInlineFragmentSelections(t *testing.T) {
}`)
})
}
+
+func TestMergeFieldSelections(t *testing.T) {
+ t.Run("depth 1", func(t *testing.T) {
+ run(t, mergeInlineFragmentSelections, testDefinition, `
+ query conflictingBecauseAlias {
+ dog {
+ extra { string }
+ extra { noString: string }
+ }
+ }`, `
+ query conflictingBecauseAlias {
+ dog {
+ extra {
+ string
+ noString: string
+ }
+ }
+ }`)
+ })
+ t.Run("depth 2", func(t *testing.T) {
+ run(t, mergeInlineFragmentSelections, testDefinition, `
+ query conflictingBecauseAlias {
+ dog {
+ extra { string }
+ extra { string: noString }
+ }
+ dog {
+ extra { string }
+ extra { string: noString }
+ }
+ }`, `
+ query conflictingBecauseAlias {
+ dog {
+ extra {
+ string
+ string: noString
+ string
+ string: noString
+ }
+ }
+ }`)
+ })
+ t.Run("aliased fields do not merge", func(t *testing.T) {
+ run(t, mergeInlineFragmentSelections, testDefinition, `
+ query conflictingBecauseAlias {
+ dog {
+ x: extras { string }
+ x: mustExtras { string }
+ }
+ }`, `
+ query conflictingBecauseAlias {
+ dog {
+ x: extras { string }
+ x: mustExtras { string }
+ }
+ }`)
+ })
+ t.Run("fields with directives should not merge when directives differ", func(t *testing.T) {
+ run(t, mergeInlineFragmentSelections, testDefinition, `
+ {
+ field @skip(if: $foo) {
+ subfieldA
+ }
+ field @skip(if: $bar) {
+ subfieldB
+ }
+ }`, `
+ {
+ field @skip(if: $foo) {
+ subfieldA
+ }
+ field @skip(if: $bar) {
+ subfieldB
+ }
+ }`)
+ })
+ t.Run("fields with identical directives merge", func(t *testing.T) {
+ run(t, mergeInlineFragmentSelections, testDefinition, `
+ {
+ field @skip(if: $foo) {
+ subfieldA
+ }
+ field @skip(if: $foo) {
+ subfieldB
+ }
+ }`, `
+ {
+ field @skip(if: $foo) {
+ subfieldA
+ subfieldB
+ }
+ }`)
+ })
+}