agent_patch
runs/2026-02-21__03-36-16__gpt-5-1-codex-mini/flux-pr-1232/flux-pr-1232.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/agent-logs/agent.patch
13888 bytes
diff --git a/tmp/agent-patch-flux-pr-1232.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/app/v2/pkg/engine/postprocess/deduplicate_single_fetches.go b/app/v2/pkg/engine/postprocess/deduplicate_single_fetches.go
index 9fb8c1c..a6fc309 100644
--- a/tmp/agent-patch-flux-pr-1232.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/app/v2/pkg/engine/postprocess/deduplicate_single_fetches.go
+++ b/app/v2/pkg/engine/postprocess/deduplicate_single_fetches.go
@@ -11,19 +11,199 @@ type deduplicateSingleFetches struct {
}
func (d *deduplicateSingleFetches) ProcessFetchTree(root *resolve.FetchTreeNode) {
- if d.disable {
+ if d.disable || root == nil {
return
}
- for i := range root.ChildNodes {
+
+ merged := make(map[int]int)
+ for i := 0; i < len(root.ChildNodes); i++ {
+ left := root.ChildNodes[i]
+ if !d.isSingleFetchNode(left) {
+ continue
+ }
for j := i + 1; j < len(root.ChildNodes); j++ {
- if root.ChildNodes[i].Item.Equals(root.ChildNodes[j].Item) {
- root.ChildNodes[i].Item.FetchPath = d.mergeFetchPath(root.ChildNodes[i].Item.FetchPath, root.ChildNodes[j].Item.FetchPath)
+ right := root.ChildNodes[j]
+ if !d.isSingleFetchNode(right) {
+ continue
+ }
+ if !left.Item.EqualsIgnoringCoordinateDependencies(right.Item) {
+ continue
+ }
+ leftFetch, ok := left.Item.Fetch.(*resolve.SingleFetch)
+ if !ok {
+ continue
+ }
+ rightFetch, ok := right.Item.Fetch.(*resolve.SingleFetch)
+ if !ok {
+ continue
+ }
+
+ merged[rightFetch.FetchDependencies.FetchID] = leftFetch.FetchDependencies.FetchID
+ left.Item.FetchPath = d.mergeFetchPath(left.Item.FetchPath, right.Item.FetchPath)
+ leftFetch.FetchDependencies.DependsOnFetchIDs = d.mergeDependsOnIDs(leftFetch.FetchDependencies.DependsOnFetchIDs, rightFetch.FetchDependencies.DependsOnFetchIDs)
+ leftFetch.FetchConfiguration.CoordinateDependencies = d.mergeCoordinateDependencies(leftFetch.FetchConfiguration.CoordinateDependencies, rightFetch.FetchConfiguration.CoordinateDependencies)
- root.ChildNodes = append(root.ChildNodes[:j], root.ChildNodes[j+1:]...)
- j--
+ root.ChildNodes = append(root.ChildNodes[:j], root.ChildNodes[j+1:]...)
+ j--
+ }
+ }
+
+ if len(merged) > 0 {
+ d.updateDependencyReferences(root, merged)
+ }
+}
+
+func (d *deduplicateSingleFetches) isSingleFetchNode(node *resolve.FetchTreeNode) bool {
+ return node != nil && node.Kind == resolve.FetchTreeNodeKindSingle && node.Item != nil && node.Item.Fetch != nil
+}
+
+func (d *deduplicateSingleFetches) mergeDependsOnIDs(left, right []int) []int {
+ if len(left) == 0 {
+ return append([]int(nil), right...)
+ }
+ if len(right) == 0 {
+ return append([]int(nil), left...)
+ }
+
+ seen := make(map[int]struct{}, len(left)+len(right))
+ out := append([]int(nil), left...)
+ for _, id := range left {
+ seen[id] = struct{}{}
+ }
+ for _, id := range right {
+ if _, ok := seen[id]; ok {
+ continue
+ }
+ seen[id] = struct{}{}
+ out = append(out, id)
+ }
+ return out
+}
+
+func (d *deduplicateSingleFetches) mergeCoordinateDependencies(left, right []resolve.FetchDependency) []resolve.FetchDependency {
+ if len(left) == 0 {
+ return append([]resolve.FetchDependency(nil), right...)
+ }
+ if len(right) == 0 {
+ return append([]resolve.FetchDependency(nil), left...)
+ }
+
+ out := append([]resolve.FetchDependency(nil), left...)
+ for _, dep := range right {
+ if d.coordinateDependencyExists(out, dep) {
+ continue
+ }
+ out = append(out, dep)
+ }
+ return out
+}
+
+func (d *deduplicateSingleFetches) coordinateDependencyExists(list []resolve.FetchDependency, candidate resolve.FetchDependency) bool {
+ for _, existing := range list {
+ if d.fetchDependencyEqual(existing, candidate) {
+ return true
+ }
+ }
+ return false
+}
+
+func (d *deduplicateSingleFetches) fetchDependencyEqual(a, b resolve.FetchDependency) bool {
+ if a.Coordinate != b.Coordinate {
+ return false
+ }
+ if a.IsUserRequested != b.IsUserRequested {
+ return false
+ }
+ return d.fetchDependencyOriginsEqual(a.DependsOn, b.DependsOn)
+}
+
+func (d *deduplicateSingleFetches) fetchDependencyOriginsEqual(a, b []resolve.FetchDependencyOrigin) bool {
+ if len(a) != len(b) {
+ return false
+ }
+ used := make([]bool, len(b))
+ for _, originA := range a {
+ matchFound := false
+ for i, originB := range b {
+ if used[i] {
+ continue
+ }
+ if d.fetchDependencyOriginEqual(originA, originB) {
+ used[i] = true
+ matchFound = true
+ break
+ }
+ }
+ if !matchFound {
+ return false
+ }
+ }
+ return true
+}
+
+func (d *deduplicateSingleFetches) fetchDependencyOriginEqual(a, b resolve.FetchDependencyOrigin) bool {
+ return a.FetchID == b.FetchID &&
+ a.Subgraph == b.Subgraph &&
+ a.Coordinate == b.Coordinate &&
+ a.IsKey == b.IsKey &&
+ a.IsRequires == b.IsRequires
+}
+
+func (d *deduplicateSingleFetches) updateDependencyReferences(root *resolve.FetchTreeNode, mapping map[int]int) {
+ if root == nil || len(mapping) == 0 {
+ return
+ }
+ d.applyMapping(root, mapping)
+}
+
+func (d *deduplicateSingleFetches) applyMapping(node *resolve.FetchTreeNode, mapping map[int]int) {
+ if node == nil {
+ return
+ }
+ if node.Item != nil {
+ if single, ok := node.Item.Fetch.(*resolve.SingleFetch); ok {
+ single.FetchDependencies.DependsOnFetchIDs = d.mapIDs(single.FetchDependencies.DependsOnFetchIDs, mapping)
+ for i := range single.FetchConfiguration.CoordinateDependencies {
+ for j := range single.FetchConfiguration.CoordinateDependencies[i].DependsOn {
+ single.FetchConfiguration.CoordinateDependencies[i].DependsOn[j].FetchID =
+ d.resolveMapping(mapping, single.FetchConfiguration.CoordinateDependencies[i].DependsOn[j].FetchID)
+ }
}
}
}
+ if node.Trigger != nil {
+ d.applyMapping(node.Trigger, mapping)
+ }
+ for _, child := range node.ChildNodes {
+ d.applyMapping(child, mapping)
+ }
+}
+
+func (d *deduplicateSingleFetches) mapIDs(list []int, mapping map[int]int) []int {
+ if len(list) == 0 {
+ return list
+ }
+ out := make([]int, 0, len(list))
+ seen := make(map[int]struct{}, len(list))
+ for _, id := range list {
+ resolved := d.resolveMapping(mapping, id)
+ if _, ok := seen[resolved]; ok {
+ continue
+ }
+ seen[resolved] = struct{}{}
+ out = append(out, resolved)
+ }
+ return out
+}
+
+func (d *deduplicateSingleFetches) resolveMapping(mapping map[int]int, id int) int {
+ for {
+ next, ok := mapping[id]
+ if !ok || next == id {
+ return id
+ }
+ id = next
+ }
}
func (d *deduplicateSingleFetches) mergeFetchPath(left, right []resolve.FetchItemPathElement) []resolve.FetchItemPathElement {
diff --git a/tmp/agent-patch-flux-pr-1232.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/app/v2/pkg/engine/postprocess/deduplicate_single_fetches_test.go b/app/v2/pkg/engine/postprocess/deduplicate_single_fetches_test.go
index 88cf2be..fa3121a 100644
--- a/tmp/agent-patch-flux-pr-1232.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/app/v2/pkg/engine/postprocess/deduplicate_single_fetches_test.go
+++ b/app/v2/pkg/engine/postprocess/deduplicate_single_fetches_test.go
@@ -115,4 +115,108 @@ func TestDeduplicateSingleFetches_ProcessFetchTree(t *testing.T) {
assert.Equal(t, output, input)
})
+
+ t.Run("ignores coordinate dependencies when deduplicating", func(t *testing.T) {
+ withCoords := resolve.Single(&resolve.SingleFetch{
+ FetchDependencies: resolve.FetchDependencies{FetchID: 1},
+ FetchConfiguration: resolve.FetchConfiguration{
+ Input: "a",
+ CoordinateDependencies: []resolve.FetchDependency{
+ {
+ Coordinate: resolve.GraphCoordinate{TypeName: "Query", FieldName: "keepField"},
+ IsUserRequested: true,
+ },
+ },
+ },
+ })
+ withAdditionalCoords := resolve.Single(&resolve.SingleFetch{
+ FetchDependencies: resolve.FetchDependencies{FetchID: 2},
+ FetchConfiguration: resolve.FetchConfiguration{
+ Input: "a",
+ CoordinateDependencies: []resolve.FetchDependency{
+ {
+ Coordinate: resolve.GraphCoordinate{TypeName: "Query", FieldName: "otherField"},
+ IsUserRequested: true,
+ },
+ {
+ Coordinate: resolve.GraphCoordinate{TypeName: "Query", FieldName: "keepField"},
+ IsUserRequested: true,
+ },
+ },
+ },
+ })
+
+ input := &resolve.FetchTreeNode{
+ ChildNodes: []*resolve.FetchTreeNode{
+ withCoords,
+ withAdditionalCoords,
+ },
+ }
+
+ dedup := &deduplicateSingleFetches{}
+ dedup.ProcessFetchTree(input)
+
+ assert.Len(t, input.ChildNodes, 1)
+ fetch := input.ChildNodes[0].Item.Fetch.(*resolve.SingleFetch)
+ assert.Equal(t, 1, fetch.FetchDependencies.FetchID)
+ assert.Len(t, fetch.FetchConfiguration.CoordinateDependencies, 2)
+
+ fields := make(map[string]struct{}, 2)
+ for _, dep := range fetch.FetchConfiguration.CoordinateDependencies {
+ fields[dep.Coordinate.FieldName] = struct{}{}
+ }
+ assert.Contains(t, fields, "keepField")
+ assert.Contains(t, fields, "otherField")
+ })
+
+ t.Run("updates dependent fetches when duplicate is removed", func(t *testing.T) {
+ keep := resolve.Single(&resolve.SingleFetch{
+ FetchDependencies: resolve.FetchDependencies{FetchID: 1},
+ FetchConfiguration: resolve.FetchConfiguration{
+ Input: "a",
+ },
+ })
+ duplicate := resolve.Single(&resolve.SingleFetch{
+ FetchDependencies: resolve.FetchDependencies{FetchID: 2},
+ FetchConfiguration: resolve.FetchConfiguration{
+ Input: "a",
+ },
+ })
+ depending := resolve.Single(&resolve.SingleFetch{
+ FetchDependencies: resolve.FetchDependencies{FetchID: 3, DependsOnFetchIDs: []int{2}},
+ FetchConfiguration: resolve.FetchConfiguration{
+ Input: "b",
+ CoordinateDependencies: []resolve.FetchDependency{
+ {
+ Coordinate: resolve.GraphCoordinate{TypeName: "Query", FieldName: "dependent"},
+ IsUserRequested: true,
+ DependsOn: []resolve.FetchDependencyOrigin{
+ {
+ FetchID: 2,
+ Subgraph: "sub",
+ Coordinate: resolve.GraphCoordinate{TypeName: "Query", FieldName: "field"},
+ },
+ },
+ },
+ },
+ },
+ })
+
+ input := &resolve.FetchTreeNode{
+ ChildNodes: []*resolve.FetchTreeNode{
+ keep,
+ duplicate,
+ depending,
+ },
+ }
+
+ dedup := &deduplicateSingleFetches{}
+ dedup.ProcessFetchTree(input)
+
+ assert.Len(t, input.ChildNodes, 2)
+ assert.Equal(t, 1, input.ChildNodes[0].Item.Fetch.(*resolve.SingleFetch).FetchDependencies.FetchID)
+ dependentFetch := input.ChildNodes[1].Item.Fetch.(*resolve.SingleFetch)
+ assert.Equal(t, []int{1}, dependentFetch.FetchDependencies.DependsOnFetchIDs)
+ assert.Equal(t, 1, dependentFetch.FetchConfiguration.CoordinateDependencies[0].DependsOn[0].FetchID)
+ })
}
diff --git a/tmp/agent-patch-flux-pr-1232.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/app/v2/pkg/engine/resolve/fetch.go b/app/v2/pkg/engine/resolve/fetch.go
index 9979256..e0d0ebe 100644
--- a/tmp/agent-patch-flux-pr-1232.1-of-1.2026-02-21__03-36-16__gpt-5-1-codex-mini/app/v2/pkg/engine/resolve/fetch.go
+++ b/app/v2/pkg/engine/resolve/fetch.go
@@ -43,6 +43,14 @@ func FetchItemWithPath(fetch Fetch, responsePath string, path ...FetchItemPathEl
}
func (f *FetchItem) Equals(other *FetchItem) bool {
+ return f.equals(other, true)
+}
+
+func (f *FetchItem) EqualsIgnoringCoordinateDependencies(other *FetchItem) bool {
+ return f.equals(other, false)
+}
+
+func (f *FetchItem) equals(other *FetchItem, includeCoordinateDependencies bool) bool {
if len(f.FetchPath) != len(other.FetchPath) {
return false
}
@@ -68,7 +76,10 @@ func (f *FetchItem) Equals(other *FetchItem) bool {
if !ok {
return false
}
- return l.FetchConfiguration.Equals(&r.FetchConfiguration)
+ if includeCoordinateDependencies {
+ return l.FetchConfiguration.Equals(&r.FetchConfiguration)
+ }
+ return l.FetchConfiguration.EqualsIgnoringCoordinateDependencies(&r.FetchConfiguration)
}
type FetchItemPathElement struct {
@@ -293,6 +304,16 @@ type FetchConfiguration struct {
}
func (fc *FetchConfiguration) Equals(other *FetchConfiguration) bool {
+ return fc.equals(other, true)
+}
+
+// EqualsIgnoringCoordinateDependencies performs the same comparison as Equals but ignores
+// coordinate-level dependency metadata, which is not relevant when deduplicating single fetches.
+func (fc *FetchConfiguration) EqualsIgnoringCoordinateDependencies(other *FetchConfiguration) bool {
+ return fc.equals(other, false)
+}
+
+func (fc *FetchConfiguration) equals(other *FetchConfiguration, includeCoordinateDependencies bool) bool {
if fc.Input != other.Input {
return false
}
@@ -319,22 +340,24 @@ func (fc *FetchConfiguration) Equals(other *FetchConfiguration) bool {
if fc.SetTemplateOutputToNullOnVariableNull != other.SetTemplateOutputToNullOnVariableNull {
return false
}
- if !slices.EqualFunc(fc.CoordinateDependencies, other.CoordinateDependencies, func(a, b FetchDependency) bool {
- if a.Coordinate != b.Coordinate {
- return false
- }
- if a.IsUserRequested != b.IsUserRequested {
+ if includeCoordinateDependencies {
+ if !slices.EqualFunc(fc.CoordinateDependencies, other.CoordinateDependencies, func(a, b FetchDependency) bool {
+ if a.Coordinate != b.Coordinate {
+ return false
+ }
+ if a.IsUserRequested != b.IsUserRequested {
+ return false
+ }
+ return slices.EqualFunc(a.DependsOn, b.DependsOn, func(x, y FetchDependencyOrigin) bool {
+ return x.FetchID == y.FetchID &&
+ x.Subgraph == y.Subgraph &&
+ x.Coordinate == y.Coordinate &&
+ x.IsKey == y.IsKey &&
+ x.IsRequires == y.IsRequires
+ })
+ }) {
return false
}
- return slices.EqualFunc(a.DependsOn, b.DependsOn, func(x, y FetchDependencyOrigin) bool {
- return x.FetchID == y.FetchID &&
- x.Subgraph == y.Subgraph &&
- x.Coordinate == y.Coordinate &&
- x.IsKey == y.IsKey &&
- x.IsRequires == y.IsRequires
- })
- }) {
- return false
}
return true
}