STET

agent_patch

runs/2026-02-28__13-38-10__gpt-5-3-codex/flux-pr-1001/flux-pr-1001.1-of-1.2026-02-28__13-38-10__gpt-5-3-codex/agent-logs/agent.patch

11428 bytes

Back to adjudication
diff --git a/tmp/agent-patch-flux-pr-1001.1-of-1.2026-02-28__13-38-10__gpt-5-3-codex/app/v2/pkg/engine/datasource/httpclient/httpclient_test.go b/app/v2/pkg/engine/datasource/httpclient/httpclient_test.go
index 223e5d8..be51e9e 100644
--- a/tmp/agent-patch-flux-pr-1001.1-of-1.2026-02-28__13-38-10__gpt-5-3-codex/app/v2/pkg/engine/datasource/httpclient/httpclient_test.go
+++ b/app/v2/pkg/engine/datasource/httpclient/httpclient_test.go
@@ -175,6 +175,38 @@ func TestHttpClientDo(t *testing.T) {
 		t.Run("net", runTest(background, input, `ok`))
 	})
 
+	t.Run("response context includes request and response metadata", func(t *testing.T) {
+		body := []byte(`{"foo":"bar"}`)
+		server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+			w.Header().Set("X-Reply", "subgraph")
+			w.WriteHeader(http.StatusCreated)
+			_, err := w.Write([]byte("ok"))
+			assert.NoError(t, err)
+		}))
+		defer server.Close()
+
+		var input []byte
+		input = SetInputMethod(input, []byte("POST"))
+		input = SetInputURL(input, []byte(server.URL))
+		input = SetInputBody(input, body)
+		input = SetInputHeader(input, []byte(`{"Authorization":["Bearer token"],"X-Custom":["value"]}`))
+		input = SetInputQueryParams(input, []byte(`[{"name":"foo","value":"bar"}]`))
+
+		ctx, responseContext := InjectResponseContext(context.Background())
+		out := &bytes.Buffer{}
+		err := Do(http.DefaultClient, ctx, input, out)
+		assert.NoError(t, err)
+		assert.Equal(t, "ok", out.String())
+
+		assert.Equal(t, http.StatusCreated, responseContext.StatusCode)
+		assert.Equal(t, "POST", responseContext.Request.Method)
+		assert.Contains(t, responseContext.Request.URL, "?foo=bar")
+		assert.Equal(t, []string{"Bearer token"}, responseContext.Request.Headers["Authorization"])
+		assert.Equal(t, "201 Created", responseContext.Response.Status)
+		assert.Equal(t, []string{"subgraph"}, responseContext.Response.Headers["X-Reply"])
+		assert.Equal(t, 2, responseContext.Response.BodySize)
+	})
+
 	t.Run("gzip", func(t *testing.T) {
 		body := []byte(`{"foo":"bar"}`)
 		server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
diff --git a/tmp/agent-patch-flux-pr-1001.1-of-1.2026-02-28__13-38-10__gpt-5-3-codex/app/v2/pkg/engine/datasource/httpclient/nethttpclient.go b/app/v2/pkg/engine/datasource/httpclient/nethttpclient.go
index 3d7adb6..4e75b3f 100644
--- a/tmp/agent-patch-flux-pr-1001.1-of-1.2026-02-28__13-38-10__gpt-5-3-codex/app/v2/pkg/engine/datasource/httpclient/nethttpclient.go
+++ b/app/v2/pkg/engine/datasource/httpclient/nethttpclient.go
@@ -70,6 +70,8 @@ type responseContextKey struct{}
 
 type ResponseContext struct {
 	StatusCode int
+	Request    TraceHTTPRequest
+	Response   TraceHTTPResponse
 }
 
 func InjectResponseContext(ctx context.Context) (context.Context, *ResponseContext) {
@@ -77,9 +79,20 @@ func InjectResponseContext(ctx context.Context) (context.Context, *ResponseConte
 	return context.WithValue(ctx, responseContextKey{}, value), value
 }
 
-func setResponseStatusCode(ctx context.Context, statusCode int) {
+func setResponseContext(ctx context.Context, request *http.Request, response *http.Response, bodySize int) {
 	if value, ok := ctx.Value(responseContextKey{}).(*ResponseContext); ok {
-		value.StatusCode = statusCode
+		value.StatusCode = response.StatusCode
+		value.Request = TraceHTTPRequest{
+			Method:  request.Method,
+			URL:     request.URL.String(),
+			Headers: request.Header.Clone(),
+		}
+		value.Response = TraceHTTPResponse{
+			StatusCode: response.StatusCode,
+			Status:     response.Status,
+			Headers:    response.Header.Clone(),
+			BodySize:   bodySize,
+		}
 	}
 }
 
@@ -191,8 +204,6 @@ func makeHTTPRequest(client *http.Client, ctx context.Context, url, method, head
 	}
 	defer response.Body.Close()
 
-	setResponseStatusCode(ctx, response.StatusCode)
-
 	respReader, err := respBodyReader(response)
 	if err != nil {
 		return err
@@ -204,7 +215,9 @@ func makeHTTPRequest(client *http.Client, ctx context.Context, url, method, head
 		} else {
 			out.Grow(1024 * 4)
 		}
-		_, err = out.ReadFrom(respReader)
+		var bytesRead int64
+		bytesRead, err = out.ReadFrom(respReader)
+		setResponseContext(ctx, request, response, int(bytesRead))
 		return
 	}
 
@@ -212,6 +225,7 @@ func makeHTTPRequest(client *http.Client, ctx context.Context, url, method, head
 	if err != nil {
 		return err
 	}
+	setResponseContext(ctx, request, response, len(data))
 	responseTrace := TraceHTTP{
 		Request: TraceHTTPRequest{
 			Method:  request.Method,
diff --git a/tmp/agent-patch-flux-pr-1001.1-of-1.2026-02-28__13-38-10__gpt-5-3-codex/app/v2/pkg/engine/resolve/loader.go b/app/v2/pkg/engine/resolve/loader.go
index 18788ee..b963f82 100644
--- a/tmp/agent-patch-flux-pr-1001.1-of-1.2026-02-28__13-38-10__gpt-5-3-codex/app/v2/pkg/engine/resolve/loader.go
+++ b/app/v2/pkg/engine/resolve/loader.go
@@ -37,7 +37,7 @@ type LoaderHooks interface {
 	// OnLoad is called before the fetch is executed
 	OnLoad(ctx context.Context, ds DataSourceInfo) context.Context
 	// OnFinished is called after the fetch has been executed and the response has been processed and merged
-	OnFinished(ctx context.Context, statusCode int, ds DataSourceInfo, err error)
+	OnFinished(ctx context.Context, responseInfo *httpclient.ResponseContext, ds DataSourceInfo, err error)
 }
 
 func IsIntrospectionDataSource(dataSourceID string) bool {
@@ -118,7 +118,7 @@ func (l *Loader) resolveParallel(nodes []*FetchTreeNode) error {
 			for j := range results[i].nestedMergeItems {
 				err = l.mergeResult(nodes[i].Item, results[i].nestedMergeItems[j], itemsItems[i][j:j+1])
 				if l.ctx.LoaderHooks != nil && results[i].nestedMergeItems[j].loaderHookContext != nil {
-					l.ctx.LoaderHooks.OnFinished(results[i].nestedMergeItems[j].loaderHookContext, results[i].nestedMergeItems[j].statusCode, results[i].nestedMergeItems[j].ds, goerrors.Join(results[i].nestedMergeItems[j].err, l.ctx.subgraphErrors))
+					l.ctx.LoaderHooks.OnFinished(results[i].nestedMergeItems[j].loaderHookContext, results[i].nestedMergeItems[j].responseContext, results[i].nestedMergeItems[j].ds, goerrors.Join(results[i].nestedMergeItems[j].err, l.ctx.subgraphErrors))
 				}
 				if err != nil {
 					return errors.WithStack(err)
@@ -127,7 +127,7 @@ func (l *Loader) resolveParallel(nodes []*FetchTreeNode) error {
 		} else {
 			err = l.mergeResult(nodes[i].Item, results[i], itemsItems[i])
 			if l.ctx.LoaderHooks != nil && results[i].loaderHookContext != nil {
-				l.ctx.LoaderHooks.OnFinished(results[i].loaderHookContext, results[i].statusCode, results[i].ds, goerrors.Join(results[i].err, l.ctx.subgraphErrors))
+				l.ctx.LoaderHooks.OnFinished(results[i].loaderHookContext, results[i].responseContext, results[i].ds, goerrors.Join(results[i].err, l.ctx.subgraphErrors))
 			}
 			if err != nil {
 				return errors.WithStack(err)
@@ -163,7 +163,7 @@ func (l *Loader) resolveSingle(item *FetchItem) error {
 		}
 		err = l.mergeResult(item, res, items)
 		if l.ctx.LoaderHooks != nil && res.loaderHookContext != nil {
-			l.ctx.LoaderHooks.OnFinished(res.loaderHookContext, res.statusCode, res.ds, goerrors.Join(res.err, l.ctx.subgraphErrors))
+			l.ctx.LoaderHooks.OnFinished(res.loaderHookContext, res.responseContext, res.ds, goerrors.Join(res.err, l.ctx.subgraphErrors))
 		}
 		return err
 	case *BatchEntityFetch:
@@ -176,7 +176,7 @@ func (l *Loader) resolveSingle(item *FetchItem) error {
 		}
 		err = l.mergeResult(item, res, items)
 		if l.ctx.LoaderHooks != nil && res.loaderHookContext != nil {
-			l.ctx.LoaderHooks.OnFinished(res.loaderHookContext, res.statusCode, res.ds, goerrors.Join(res.err, l.ctx.subgraphErrors))
+			l.ctx.LoaderHooks.OnFinished(res.loaderHookContext, res.responseContext, res.ds, goerrors.Join(res.err, l.ctx.subgraphErrors))
 		}
 		return err
 	case *EntityFetch:
@@ -189,7 +189,7 @@ func (l *Loader) resolveSingle(item *FetchItem) error {
 		}
 		err = l.mergeResult(item, res, items)
 		if l.ctx.LoaderHooks != nil && res.loaderHookContext != nil {
-			l.ctx.LoaderHooks.OnFinished(res.loaderHookContext, res.statusCode, res.ds, goerrors.Join(res.err, l.ctx.subgraphErrors))
+			l.ctx.LoaderHooks.OnFinished(res.loaderHookContext, res.responseContext, res.ds, goerrors.Join(res.err, l.ctx.subgraphErrors))
 		}
 		return err
 	case *ParallelListItemFetch:
@@ -222,7 +222,7 @@ func (l *Loader) resolveSingle(item *FetchItem) error {
 		for i := range results {
 			err = l.mergeResult(item, results[i], items[i:i+1])
 			if l.ctx.LoaderHooks != nil && results[i].loaderHookContext != nil {
-				l.ctx.LoaderHooks.OnFinished(results[i].loaderHookContext, results[i].statusCode, results[i].ds, goerrors.Join(results[i].err, l.ctx.subgraphErrors))
+				l.ctx.LoaderHooks.OnFinished(results[i].loaderHookContext, results[i].responseContext, results[i].ds, goerrors.Join(results[i].err, l.ctx.subgraphErrors))
 			}
 			if err != nil {
 				return errors.WithStack(err)
@@ -542,6 +542,7 @@ type result struct {
 	statusCode int
 	err        error
 	ds         DataSourceInfo
+	responseContext *httpclient.ResponseContext
 
 	authorizationRejected        bool
 	authorizationRejectedReasons []string
@@ -1533,6 +1534,7 @@ func (l *Loader) executeSourceLoad(ctx context.Context, fetchItem *FetchItem, so
 	}
 
 	res.statusCode = responseContext.StatusCode
+	res.responseContext = responseContext
 
 	if l.ctx.TracingOptions.Enable {
 		stats := GetSingleFlightStats(ctx)
diff --git a/tmp/agent-patch-flux-pr-1001.1-of-1.2026-02-28__13-38-10__gpt-5-3-codex/app/v2/pkg/engine/resolve/loader_hooks_test.go b/app/v2/pkg/engine/resolve/loader_hooks_test.go
index d77c614..afb8995 100644
--- a/tmp/agent-patch-flux-pr-1001.1-of-1.2026-02-28__13-38-10__gpt-5-3-codex/app/v2/pkg/engine/resolve/loader_hooks_test.go
+++ b/app/v2/pkg/engine/resolve/loader_hooks_test.go
@@ -11,21 +11,24 @@ import (
 	"github.com/golang/mock/gomock"
 	"github.com/stretchr/testify/assert"
 	"github.com/wundergraph/graphql-go-tools/v2/pkg/ast"
+	"github.com/wundergraph/graphql-go-tools/v2/pkg/engine/datasource/httpclient"
 )
 
 type TestLoaderHooks struct {
-	preFetchCalls  atomic.Int64
-	postFetchCalls atomic.Int64
-	errors         []error
-	mu             sync.Mutex
+	preFetchCalls    atomic.Int64
+	postFetchCalls   atomic.Int64
+	errors           []error
+	responseContexts []*httpclient.ResponseContext
+	mu               sync.Mutex
 }
 
 func NewTestLoaderHooks() LoaderHooks {
 	return &TestLoaderHooks{
-		preFetchCalls:  atomic.Int64{},
-		postFetchCalls: atomic.Int64{},
-		errors:         make([]error, 0),
-		mu:             sync.Mutex{},
+		preFetchCalls:    atomic.Int64{},
+		postFetchCalls:   atomic.Int64{},
+		errors:           make([]error, 0),
+		responseContexts: make([]*httpclient.ResponseContext, 0),
+		mu:               sync.Mutex{},
 	}
 }
 
@@ -35,13 +38,14 @@ func (f *TestLoaderHooks) OnLoad(ctx context.Context, ds DataSourceInfo) context
 	return ctx
 }
 
-func (f *TestLoaderHooks) OnFinished(ctx context.Context, statusCode int, ds DataSourceInfo, err error) {
+func (f *TestLoaderHooks) OnFinished(ctx context.Context, responseInfo *httpclient.ResponseContext, ds DataSourceInfo, err error) {
 	f.postFetchCalls.Add(1)
 
 	f.mu.Lock()
 	defer f.mu.Unlock()
 
 	f.errors = append(f.errors, err)
+	f.responseContexts = append(f.responseContexts, responseInfo)
 }
 
 func TestLoaderHooks_FetchPipeline(t *testing.T) {