STET

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

Back to adjudication
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)
+	})
 }