STET

agent_patch

runs/2026-02-28__13-38-10__gpt-5-4/flux-pr-870/flux-pr-870.1-of-1.2026-02-28__13-38-10__gpt-5-4/agent-logs/agent.patch

10494 bytes

Back to adjudication
diff --git a/tmp/agent-patch-flux-pr-870.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/resolve/extensions_test.go b/app/v2/pkg/engine/resolve/extensions_test.go
index 6d16e3e..c5a30d3 100644
--- a/tmp/agent-patch-flux-pr-870.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/resolve/extensions_test.go
+++ b/app/v2/pkg/engine/resolve/extensions_test.go
@@ -120,7 +120,7 @@ func TestExtensions(t *testing.T) {
 		ctx.ctx = SetTraceStart(ctx.ctx, true)
 
 		return res, ctx,
-			`{"errors":[{"message":"Unauthorized request to Subgraph 'users' at Path 'query', Reason: test."},{"message":"Failed to fetch from Subgraph 'reviews' at Path 'query.me'.","extensions":{"errors":[{"message":"Failed to render Fetch Input","path":["me"]}]}},{"message":"Failed to fetch from Subgraph 'products' at Path 'query.me.reviews.@.product'.","extensions":{"errors":[{"message":"Failed to render Fetch Input","path":["me","reviews","@","product"]}]}}],"data":{"me":null},"extensions":{"authorization":{"missingScopes":[["read:users"]]},"rateLimit":{"Policy":"policy","Allowed":0,"Used":0},"trace":{"version":"1","info":{"trace_start_time":"","trace_start_unix":0,"parse_stats":{"duration_nanoseconds":0,"duration_pretty":"","duration_since_start_nanoseconds":0,"duration_since_start_pretty":""},"normalize_stats":{"duration_nanoseconds":0,"duration_pretty":"","duration_since_start_nanoseconds":0,"duration_since_start_pretty":""},"validate_stats":{"duration_nanoseconds":0,"duration_pretty":"","duration_since_start_nanoseconds":0,"duration_since_start_pretty":""},"planner_stats":{"duration_nanoseconds":0,"duration_pretty":"","duration_since_start_nanoseconds":0,"duration_since_start_pretty":""}},"fetches":{"kind":"Sequence","children":[{"kind":"Single","fetch":{"kind":"Single","path":"query","trace":{"raw_input_data":{},"single_flight_used":false,"single_flight_shared_response":false,"load_skipped":false}}},{"kind":"Single","fetch":{"kind":"Single","path":"query.me","trace":{"single_flight_used":false,"single_flight_shared_response":false,"load_skipped":false}}},{"kind":"Single","fetch":{"kind":"Single","path":"query.me.reviews.@.product","trace":{"single_flight_used":false,"single_flight_shared_response":false,"load_skipped":false}}}]}}}}`,
+			`{"errors":[{"message":"Unauthorized request to Subgraph 'users' at Path 'query', Reason: test."},{"message":"Failed to fetch from Subgraph 'reviews' at Path 'query.me'.","extensions":{"errors":[{"message":"Failed to render Fetch Input","path":["me"]}]}},{"message":"Failed to fetch from Subgraph 'products' at Path 'query.me.reviews.@.product'.","extensions":{"errors":[{"message":"Failed to render Fetch Input","path":["me","reviews","@","product"]}]}}],"data":{"me":null},"extensions":{"authorization":{"missingScopes":[["read:users"]]},"rateLimit":{"Policy":"policy","Allowed":0,"Used":0},"trace":{"version":"1","info":{"trace_start_time":"","trace_start_unix":0,"parse_stats":{"duration_nanoseconds":0,"duration_pretty":"","duration_since_start_nanoseconds":0,"duration_since_start_pretty":""},"normalize_stats":{"duration_nanoseconds":0,"duration_pretty":"","duration_since_start_nanoseconds":0,"duration_since_start_pretty":""},"validate_stats":{"duration_nanoseconds":0,"duration_pretty":"","duration_since_start_nanoseconds":0,"duration_since_start_pretty":""},"planner_stats":{"duration_nanoseconds":0,"duration_pretty":"","duration_since_start_nanoseconds":0,"duration_since_start_pretty":""}},"fetches":{"kind":"Sequence","children":[{"kind":"Single","fetch":{"kind":"Single","path":"query","data_source_id":"users","trace":{"raw_input_data":{},"single_flight_used":false,"single_flight_shared_response":false,"load_skipped":false}}},{"kind":"Single","fetch":{"kind":"Single","path":"query.me","data_source_id":"reviews","trace":{"single_flight_used":false,"single_flight_shared_response":false,"load_skipped":false}}},{"kind":"Single","fetch":{"kind":"Single","path":"query.me.reviews.@.product","data_source_id":"products","trace":{"single_flight_used":false,"single_flight_shared_response":false,"load_skipped":false}}}]}}}}`,
 			func(t *testing.T) {}
 	}))
 }
diff --git a/tmp/agent-patch-flux-pr-870.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/resolve/fetch.go b/app/v2/pkg/engine/resolve/fetch.go
index 138664e..650f089 100644
--- a/tmp/agent-patch-flux-pr-870.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/resolve/fetch.go
+++ b/app/v2/pkg/engine/resolve/fetch.go
@@ -1,6 +1,7 @@
 package resolve
 
 import (
+	"bytes"
 	"encoding/json"
 	"slices"
 
@@ -291,6 +292,39 @@ type DataSourceLoadTrace struct {
 	Path                       string          `json:"-"`
 }
 
+func (d DataSourceLoadTrace) MarshalJSON() ([]byte, error) {
+	type dataSourceLoadTraceAlias DataSourceLoadTrace
+
+	alias := dataSourceLoadTraceAlias(d)
+	alias.RawInputData = normalizeTraceJSON(alias.RawInputData)
+	alias.Input = normalizeTraceJSON(alias.Input)
+	alias.Output = normalizeTraceJSON(alias.Output)
+
+	return json.Marshal(alias)
+}
+
+func normalizeTraceJSON(raw json.RawMessage) json.RawMessage {
+	raw = bytes.TrimSpace(raw)
+	if len(raw) == 0 {
+		return nil
+	}
+
+	var value any
+	if err := json.Unmarshal(raw, &value); err == nil {
+		normalized, err := json.Marshal(value)
+		if err == nil {
+			return normalized
+		}
+	}
+
+	var compact bytes.Buffer
+	if err := json.Compact(&compact, raw); err == nil {
+		return append(json.RawMessage(nil), compact.Bytes()...)
+	}
+
+	return append(json.RawMessage(nil), raw...)
+}
+
 type LoadStats struct {
 	GetConn              GetConnStats              `json:"get_conn"`
 	GotConn              GotConnStats              `json:"got_conn"`
diff --git a/tmp/agent-patch-flux-pr-870.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/resolve/fetchtree.go b/app/v2/pkg/engine/resolve/fetchtree.go
index b246410..e9c68fa 100644
--- a/tmp/agent-patch-flux-pr-870.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/resolve/fetchtree.go
+++ b/app/v2/pkg/engine/resolve/fetchtree.go
@@ -78,10 +78,12 @@ type FetchTreeTraceNode struct {
 }
 
 type FetchTraceNode struct {
-	Kind    string                 `json:"kind"`
-	Path    string                 `json:"path"`
-	Fetch   *DataSourceLoadTrace   `json:"trace,omitempty"`
-	Fetches []*DataSourceLoadTrace `json:"traces,omitempty"`
+	Kind                 string                 `json:"kind"`
+	Path                 string                 `json:"path"`
+	DataSourceID         string                 `json:"data_source_id,omitempty"`
+	DataSourceIdentifier string                 `json:"data_source_identifier,omitempty"`
+	Fetch                *DataSourceLoadTrace   `json:"trace,omitempty"`
+	Fetches              []*DataSourceLoadTrace `json:"traces,omitempty"`
 }
 
 func (n *FetchTreeNode) Trace() *FetchTreeTraceNode {
@@ -96,26 +98,34 @@ func (n *FetchTreeNode) Trace() *FetchTreeTraceNode {
 		switch f := n.Item.Fetch.(type) {
 		case *SingleFetch:
 			trace.Fetch = &FetchTraceNode{
-				Kind:  "Single",
-				Fetch: f.Trace,
-				Path:  n.Item.ResponsePath,
+				Kind:                 "Single",
+				Fetch:                f.Trace,
+				Path:                 n.Item.ResponsePath,
+				DataSourceID:         fetchDataSourceID(f.Info),
+				DataSourceIdentifier: string(f.DataSourceIdentifier),
 			}
 		case *EntityFetch:
 			trace.Fetch = &FetchTraceNode{
-				Kind:  "Entity",
-				Fetch: f.Trace,
-				Path:  n.Item.ResponsePath,
+				Kind:                 "Entity",
+				Fetch:                f.Trace,
+				Path:                 n.Item.ResponsePath,
+				DataSourceID:         fetchDataSourceID(f.Info),
+				DataSourceIdentifier: string(f.DataSourceIdentifier),
 			}
 		case *BatchEntityFetch:
 			trace.Fetch = &FetchTraceNode{
-				Kind:  "BatchEntity",
-				Fetch: f.Trace,
-				Path:  n.Item.ResponsePath,
+				Kind:                 "BatchEntity",
+				Fetch:                f.Trace,
+				Path:                 n.Item.ResponsePath,
+				DataSourceID:         fetchDataSourceID(f.Info),
+				DataSourceIdentifier: string(f.DataSourceIdentifier),
 			}
 		case *ParallelListItemFetch:
 			trace.Fetch = &FetchTraceNode{
-				Kind:    "ParallelList",
-				Fetches: make([]*DataSourceLoadTrace, len(f.Traces)),
+				Kind:                 "ParallelList",
+				DataSourceID:         fetchDataSourceID(f.Fetch.Info),
+				DataSourceIdentifier: string(f.Fetch.DataSourceIdentifier),
+				Fetches:              make([]*DataSourceLoadTrace, len(f.Traces)),
 			}
 			for i, t := range f.Traces {
 				trace.Fetch.Fetches[i] = t.Trace
@@ -130,3 +140,10 @@ func (n *FetchTreeNode) Trace() *FetchTreeTraceNode {
 	}
 	return trace
 }
+
+func fetchDataSourceID(info *FetchInfo) string {
+	if info == nil {
+		return ""
+	}
+	return info.DataSourceID
+}
diff --git a/app/v2/pkg/engine/resolve/trace_test.go b/app/v2/pkg/engine/resolve/trace_test.go
new file mode 100644
index 0000000..c13a667
--- /dev/null
+++ b/app/v2/pkg/engine/resolve/trace_test.go
@@ -0,0 +1,44 @@
+package resolve
+
+import (
+	"encoding/json"
+	"testing"
+)
+
+func TestDataSourceLoadTraceMarshalJSONNormalizesEmbeddedJSON(t *testing.T) {
+	trace := DataSourceLoadTrace{
+		RawInputData: json.RawMessage(`{ "a": 1, "a": 2, "nested": { "b": 1, "b": 3 } }`),
+		Input:        json.RawMessage("{\n  \"query\": \"{ me }\"\n}"),
+		Output:       json.RawMessage(`{ "data": { "me": { "id": 1, "id": 2 } } }`),
+	}
+
+	data, err := json.Marshal(trace)
+	if err != nil {
+		t.Fatalf("marshal trace: %v", err)
+	}
+
+	expected := `{"raw_input_data":{"a":2,"nested":{"b":3}},"input":{"query":"{ me }"},"output":{"data":{"me":{"id":2}}},"single_flight_used":false,"single_flight_shared_response":false,"load_skipped":false}`
+	if string(data) != expected {
+		t.Fatalf("unexpected trace json:\nexpected: %s\nactual:   %s", expected, string(data))
+	}
+}
+
+func TestFetchTreeTraceIncludesDataSourceMetadata(t *testing.T) {
+	tree := SingleWithPath(&SingleFetch{
+		DataSourceIdentifier: []byte("graphql_datasource.Source"),
+		Info: &FetchInfo{
+			DataSourceID: "products",
+		},
+	}, "query.topProducts")
+
+	trace := tree.Trace()
+	if trace == nil || trace.Fetch == nil {
+		t.Fatal("expected fetch trace")
+	}
+	if trace.Fetch.DataSourceID != "products" {
+		t.Fatalf("expected data source id %q, got %q", "products", trace.Fetch.DataSourceID)
+	}
+	if trace.Fetch.DataSourceIdentifier != "graphql_datasource.Source" {
+		t.Fatalf("expected data source identifier %q, got %q", "graphql_datasource.Source", trace.Fetch.DataSourceIdentifier)
+	}
+}