agent_patch
runs/2026-02-28__13-38-10__gpt-5-3-codex/flux-pr-1232/flux-pr-1232.1-of-1.2026-02-28__13-38-10__gpt-5-3-codex/agent-logs/agent.patch
9805 bytes
diff --git a/tmp/agent-patch-flux-pr-1232.1-of-1.2026-02-28__13-38-10__gpt-5-3-codex/app/v2/pkg/engine/postprocess/deduplicate_single_fetches.go b/app/v2/pkg/engine/postprocess/deduplicate_single_fetches.go
index 9fb8c1c..dba9442 100644
--- a/tmp/agent-patch-flux-pr-1232.1-of-1.2026-02-28__13-38-10__gpt-5-3-codex/app/v2/pkg/engine/postprocess/deduplicate_single_fetches.go
+++ b/app/v2/pkg/engine/postprocess/deduplicate_single_fetches.go
@@ -16,16 +16,55 @@ func (d *deduplicateSingleFetches) ProcessFetchTree(root *resolve.FetchTreeNode)
}
for i := range root.ChildNodes {
for j := i + 1; j < len(root.ChildNodes); j++ {
- if root.ChildNodes[i].Item.Equals(root.ChildNodes[j].Item) {
+ if d.equalsForDeduplication(root.ChildNodes[i].Item, root.ChildNodes[j].Item) {
+ keptFetchID := root.ChildNodes[i].Item.Fetch.Dependencies().FetchID
+ removedFetchID := root.ChildNodes[j].Item.Fetch.Dependencies().FetchID
+
root.ChildNodes[i].Item.FetchPath = d.mergeFetchPath(root.ChildNodes[i].Item.FetchPath, root.ChildNodes[j].Item.FetchPath)
+ d.mergeDependsOnFetchIDs(root.ChildNodes[i].Item.Fetch, root.ChildNodes[j].Item.Fetch)
root.ChildNodes = append(root.ChildNodes[:j], root.ChildNodes[j+1:]...)
+ d.replaceFetchIDReferences(root, removedFetchID, keptFetchID)
j--
}
}
}
}
+func (d *deduplicateSingleFetches) equalsForDeduplication(left, right *resolve.FetchItem) bool {
+ if len(left.FetchPath) != len(right.FetchPath) {
+ return false
+ }
+
+ for i := range left.FetchPath {
+ if left.FetchPath[i].Kind != right.FetchPath[i].Kind {
+ return false
+ }
+
+ if !slices.Equal(left.FetchPath[i].Path, right.FetchPath[i].Path) {
+ return false
+ }
+ }
+
+ leftSingleFetch, ok := left.Fetch.(*resolve.SingleFetch)
+ if !ok {
+ return false
+ }
+ rightSingleFetch, ok := right.Fetch.(*resolve.SingleFetch)
+ if !ok {
+ return false
+ }
+
+ // Coordinate dependencies are metadata and include fetch-id-level details that should not block
+ // deduplication of functionally identical single fetches.
+ leftConfig := leftSingleFetch.FetchConfiguration
+ rightConfig := rightSingleFetch.FetchConfiguration
+ leftConfig.CoordinateDependencies = nil
+ rightConfig.CoordinateDependencies = nil
+
+ return leftConfig.Equals(&rightConfig)
+}
+
func (d *deduplicateSingleFetches) mergeFetchPath(left, right []resolve.FetchItemPathElement) []resolve.FetchItemPathElement {
for i := range left {
left[i].TypeNames = d.mergeTypeNames(left[i].TypeNames, right[i].TypeNames)
@@ -44,3 +83,68 @@ func (d *deduplicateSingleFetches) mergeTypeNames(left []string, right []string)
slices.Sort(out)
return slices.Compact(out) // removes consecutive duplicates from the sorted slice
}
+
+func (d *deduplicateSingleFetches) mergeDependsOnFetchIDs(kept, removed resolve.Fetch) {
+ left := kept.Dependencies().DependsOnFetchIDs
+ right := removed.Dependencies().DependsOnFetchIDs
+ if len(left) == 0 && len(right) == 0 {
+ return
+ }
+ out := make([]int, 0, len(left)+len(right))
+ out = append(out, left...)
+ out = append(out, right...)
+ slices.Sort(out)
+ out = slices.Compact(out)
+
+ switch f := kept.(type) {
+ case *resolve.SingleFetch:
+ f.FetchDependencies.DependsOnFetchIDs = out
+ case *resolve.EntityFetch:
+ f.FetchDependencies.DependsOnFetchIDs = out
+ case *resolve.BatchEntityFetch:
+ f.FetchDependencies.DependsOnFetchIDs = out
+ case *resolve.ParallelListItemFetch:
+ f.Fetch.FetchDependencies.DependsOnFetchIDs = out
+ }
+}
+
+func (d *deduplicateSingleFetches) replaceFetchIDReferences(root *resolve.FetchTreeNode, oldID, newID int) {
+ for _, node := range root.ChildNodes {
+ deps := node.Item.Fetch.Dependencies().DependsOnFetchIDs
+ if len(deps) > 0 {
+ updated := make([]int, 0, len(deps))
+ for _, dep := range deps {
+ if dep == oldID {
+ dep = newID
+ }
+ updated = append(updated, dep)
+ }
+ slices.Sort(updated)
+ updated = slices.Compact(updated)
+ d.setDependsOnFetchIDs(node.Item.Fetch, updated)
+ }
+
+ if singleFetch, ok := node.Item.Fetch.(*resolve.SingleFetch); ok {
+ for i := range singleFetch.FetchConfiguration.CoordinateDependencies {
+ for j := range singleFetch.FetchConfiguration.CoordinateDependencies[i].DependsOn {
+ if singleFetch.FetchConfiguration.CoordinateDependencies[i].DependsOn[j].FetchID == oldID {
+ singleFetch.FetchConfiguration.CoordinateDependencies[i].DependsOn[j].FetchID = newID
+ }
+ }
+ }
+ }
+ }
+}
+
+func (d *deduplicateSingleFetches) setDependsOnFetchIDs(fetch resolve.Fetch, dependsOn []int) {
+ switch f := fetch.(type) {
+ case *resolve.SingleFetch:
+ f.FetchDependencies.DependsOnFetchIDs = dependsOn
+ case *resolve.EntityFetch:
+ f.FetchDependencies.DependsOnFetchIDs = dependsOn
+ case *resolve.BatchEntityFetch:
+ f.FetchDependencies.DependsOnFetchIDs = dependsOn
+ case *resolve.ParallelListItemFetch:
+ f.Fetch.FetchDependencies.DependsOnFetchIDs = dependsOn
+ }
+}
diff --git a/tmp/agent-patch-flux-pr-1232.1-of-1.2026-02-28__13-38-10__gpt-5-3-codex/app/v2/pkg/engine/postprocess/deduplicate_single_fetches_test.go b/app/v2/pkg/engine/postprocess/deduplicate_single_fetches_test.go
index 88cf2be..549c266 100644
--- a/tmp/agent-patch-flux-pr-1232.1-of-1.2026-02-28__13-38-10__gpt-5-3-codex/app/v2/pkg/engine/postprocess/deduplicate_single_fetches_test.go
+++ b/app/v2/pkg/engine/postprocess/deduplicate_single_fetches_test.go
@@ -115,4 +115,132 @@ func TestDeduplicateSingleFetches_ProcessFetchTree(t *testing.T) {
assert.Equal(t, output, input)
})
+
+ t.Run("remaps dependent fetch ids after removing duplicate", func(t *testing.T) {
+ input := &resolve.FetchTreeNode{
+ ChildNodes: []*resolve.FetchTreeNode{
+ {
+ Kind: resolve.FetchTreeNodeKindSingle,
+ Item: &resolve.FetchItem{
+ Fetch: &resolve.SingleFetch{
+ FetchDependencies: resolve.FetchDependencies{
+ FetchID: 1,
+ DependsOnFetchIDs: []int{10},
+ },
+ FetchConfiguration: resolve.FetchConfiguration{
+ Input: "a",
+ },
+ },
+ },
+ },
+ {
+ Kind: resolve.FetchTreeNodeKindSingle,
+ Item: &resolve.FetchItem{
+ Fetch: &resolve.SingleFetch{
+ FetchDependencies: resolve.FetchDependencies{
+ FetchID: 2,
+ DependsOnFetchIDs: []int{11},
+ },
+ FetchConfiguration: resolve.FetchConfiguration{
+ Input: "a",
+ },
+ },
+ },
+ },
+ {
+ Kind: resolve.FetchTreeNodeKindSingle,
+ Item: &resolve.FetchItem{
+ Fetch: &resolve.SingleFetch{
+ FetchDependencies: resolve.FetchDependencies{
+ FetchID: 3,
+ DependsOnFetchIDs: []int{2},
+ },
+ FetchConfiguration: resolve.FetchConfiguration{
+ Input: "c",
+ CoordinateDependencies: []resolve.FetchDependency{
+ {
+ Coordinate: resolve.GraphCoordinate{TypeName: "Query", FieldName: "x"},
+ IsUserRequested: true,
+ DependsOn: []resolve.FetchDependencyOrigin{
+ {
+ FetchID: 2,
+ Subgraph: "accounts",
+ Coordinate: resolve.GraphCoordinate{TypeName: "User", FieldName: "id"},
+ IsKey: true,
+ IsRequires: false,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+
+ dedup := &deduplicateSingleFetches{}
+ dedup.ProcessFetchTree(input)
+
+ assert.Len(t, input.ChildNodes, 2)
+ kept := input.ChildNodes[0].Item.Fetch.(*resolve.SingleFetch)
+ downstream := input.ChildNodes[1].Item.Fetch.(*resolve.SingleFetch)
+ assert.Equal(t, []int{10, 11}, kept.DependsOnFetchIDs)
+ assert.Equal(t, []int{1}, downstream.DependsOnFetchIDs)
+ assert.Equal(t, 1, downstream.CoordinateDependencies[0].DependsOn[0].FetchID)
+ })
+
+ t.Run("ignores coordinate dependency metadata in dedupe equality", func(t *testing.T) {
+ input := &resolve.FetchTreeNode{
+ ChildNodes: []*resolve.FetchTreeNode{
+ {
+ Kind: resolve.FetchTreeNodeKindSingle,
+ Item: &resolve.FetchItem{
+ FetchPath: []resolve.FetchItemPathElement{{Kind: resolve.FetchItemPathElementKindObject, Path: []string{"a"}}},
+ Fetch: &resolve.SingleFetch{
+ FetchDependencies: resolve.FetchDependencies{FetchID: 1},
+ FetchConfiguration: resolve.FetchConfiguration{
+ Input: "a",
+ CoordinateDependencies: []resolve.FetchDependency{
+ {
+ Coordinate: resolve.GraphCoordinate{TypeName: "A", FieldName: "id"},
+ IsUserRequested: true,
+ DependsOn: []resolve.FetchDependencyOrigin{
+ {FetchID: 7, Subgraph: "accounts", Coordinate: resolve.GraphCoordinate{TypeName: "User", FieldName: "id"}, IsKey: true},
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ Kind: resolve.FetchTreeNodeKindSingle,
+ Item: &resolve.FetchItem{
+ FetchPath: []resolve.FetchItemPathElement{{Kind: resolve.FetchItemPathElementKindObject, Path: []string{"a"}}},
+ Fetch: &resolve.SingleFetch{
+ FetchDependencies: resolve.FetchDependencies{FetchID: 2},
+ FetchConfiguration: resolve.FetchConfiguration{
+ Input: "a",
+ CoordinateDependencies: []resolve.FetchDependency{
+ {
+ Coordinate: resolve.GraphCoordinate{TypeName: "A", FieldName: "id"},
+ IsUserRequested: true,
+ DependsOn: []resolve.FetchDependencyOrigin{
+ {FetchID: 8, Subgraph: "products", Coordinate: resolve.GraphCoordinate{TypeName: "Product", FieldName: "sku"}, IsRequires: true},
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+
+ dedup := &deduplicateSingleFetches{}
+ dedup.ProcessFetchTree(input)
+
+ assert.Len(t, input.ChildNodes, 1)
+ })
}