STET

agent_patch

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

14034 bytes

Back to adjudication
diff --git a/tmp/agent-patch-flux-pr-1209.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/execution/engine/execution_engine_grpc_test.go b/app/execution/engine/execution_engine_grpc_test.go
index 15e0a26..0e8055a 100644
--- a/tmp/agent-patch-flux-pr-1209.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/execution/engine/execution_engine_grpc_test.go
+++ b/app/execution/engine/execution_engine_grpc_test.go
@@ -259,6 +259,27 @@ func TestGRPCSubgraphExecution(t *testing.T) {
 		require.Equal(t, `{"data":{"user":{"id":"1","name":"User 1"}}}`, response)
 	})
 
+	t.Run("should preserve GraphQL aliases in grpc responses", func(t *testing.T) {
+		operation := graphql.Request{
+			OperationName: "UserAliasQuery",
+			Variables: stringify(map[string]any{
+				"id": "1",
+			}),
+			Query: `
+				query UserAliasQuery($id: ID!) {
+					aliasedUser: user(id: $id) {
+						myId: id
+						myName: name
+					}
+				}
+			`,
+		}
+
+		response, err := executeOperation(t, conn, operation, withGRPCMapping(mapping.DefaultGRPCMapping()))
+		require.NoError(t, err)
+		require.Equal(t, `{"data":{"aliasedUser":{"myId":"1","myName":"User 1"}}}`, response)
+	})
+
 	t.Run("should run complex query", func(t *testing.T) {
 		operation := graphql.Request{
 			OperationName: "ComplexFilterTypeQuery",
@@ -390,6 +411,37 @@ func TestGRPCSubgraphExecution(t *testing.T) {
 		require.Error(t, err)
 	})
 
+	t.Run("should resolve union member types for mapped mutation rpcs", func(t *testing.T) {
+		operation := graphql.Request{
+			OperationName: "PerformActionMutation",
+			Variables: stringify(map[string]any{
+				"input": map[string]any{
+					"type":    "error_action",
+					"payload": "invalid data",
+				},
+			}),
+			Query: `
+				mutation PerformActionMutation($input: ActionInput!) {
+					result: performAction(input: $input) {
+						__typename
+						... on ActionSuccess {
+							message
+							timestamp
+						}
+						... on ActionError {
+							message
+							code
+						}
+					}
+				}
+			`,
+		}
+
+		response, err := executeOperation(t, conn, operation, withGRPCMapping(mapping.DefaultGRPCMapping()))
+		require.NoError(t, err)
+		require.Equal(t, `{"data":{"result":{"__typename":"ActionError","message":"Action failed due to validation error","code":"VALIDATION_ERROR"}}}`, response)
+	})
+
 	// Category tests to verify enum handling
 	t.Run("should correctly handle query for all categories with enum values", func(t *testing.T) {
 		operation := graphql.Request{
diff --git a/tmp/agent-patch-flux-pr-1209.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/datasource/grpc_datasource/compiler.go b/app/v2/pkg/engine/datasource/grpc_datasource/compiler.go
index 1b519b7..8b4291b 100644
--- a/tmp/agent-patch-flux-pr-1209.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/datasource/grpc_datasource/compiler.go
+++ b/app/v2/pkg/engine/datasource/grpc_datasource/compiler.go
@@ -322,8 +322,14 @@ func (p *RPCCompiler) Compile(executionPlan *RPCExecutionPlan, inputData gjson.R
 	invocations := make([]Invocation, 0, len(executionPlan.Calls))
 
 	for _, call := range executionPlan.Calls {
-		inputMessage := p.doc.MessageByName(call.Request.Name)
-		outputMessage := p.doc.MessageByName(call.Response.Name)
+		inputMessage, ok := p.resolveMessage(call.Request.Name)
+		if !ok {
+			return nil, fmt.Errorf("failed to compile invocation for method %s: request message %s not found in protobuf schema", call.MethodName, call.Request.Name)
+		}
+		outputMessage, ok := p.resolveMessage(call.Response.Name)
+		if !ok {
+			return nil, fmt.Errorf("failed to compile invocation for method %s: response message %s not found in protobuf schema", call.MethodName, call.Response.Name)
+		}
 
 		request := p.buildProtoMessage(inputMessage, &call.Request, inputData)
 		response := p.newEmptyMessage(outputMessage)
@@ -349,6 +355,15 @@ func (p *RPCCompiler) Compile(executionPlan *RPCExecutionPlan, inputData gjson.R
 	return invocations, nil
 }
 
+func (p *RPCCompiler) resolveMessage(name string) (Message, bool) {
+	ref := p.doc.MessageRefByName(name)
+	if ref == -1 {
+		return Message{}, false
+	}
+
+	return p.doc.MessageByRef(ref), true
+}
+
 func (p *RPCCompiler) resolveServiceName(methodName string) (string, bool) {
 	for _, service := range p.doc.Services {
 		for _, methodRef := range service.MethodsRefs {
diff --git a/tmp/agent-patch-flux-pr-1209.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/datasource/grpc_datasource/compiler_test.go b/app/v2/pkg/engine/datasource/grpc_datasource/compiler_test.go
index 5e098c3..ecc53d8 100644
--- a/tmp/agent-patch-flux-pr-1209.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/datasource/grpc_datasource/compiler_test.go
+++ b/app/v2/pkg/engine/datasource/grpc_datasource/compiler_test.go
@@ -291,3 +291,43 @@ func TestBuildProtoMessage(t *testing.T) {
 	require.Equal(t, 1, len(invocations))
 
 }
+
+func TestCompileReturnsClearErrorForMissingRequestMessage(t *testing.T) {
+	compiler, err := NewProtoCompiler(grpctest.MustProtoSchema(t), nil)
+	require.NoError(t, err)
+
+	_, err = compiler.Compile(&RPCExecutionPlan{
+		Calls: []RPCCall{
+			{
+				MethodName: "QueryUser",
+				Request: RPCMessage{
+					Name: "DoesNotExistRequest",
+				},
+				Response: RPCMessage{
+					Name: "QueryUserResponse",
+				},
+			},
+		},
+	}, gjson.Parse(`{}`))
+	require.EqualError(t, err, "failed to compile invocation for method QueryUser: request message DoesNotExistRequest not found in protobuf schema")
+}
+
+func TestCompileReturnsClearErrorForMissingResponseMessage(t *testing.T) {
+	compiler, err := NewProtoCompiler(grpctest.MustProtoSchema(t), nil)
+	require.NoError(t, err)
+
+	_, err = compiler.Compile(&RPCExecutionPlan{
+		Calls: []RPCCall{
+			{
+				MethodName: "QueryUser",
+				Request: RPCMessage{
+					Name: "QueryUserRequest",
+				},
+				Response: RPCMessage{
+					Name: "DoesNotExistResponse",
+				},
+			},
+		},
+	}, gjson.Parse(`{}`))
+	require.EqualError(t, err, "failed to compile invocation for method QueryUser: response message DoesNotExistResponse not found in protobuf schema")
+}
diff --git a/tmp/agent-patch-flux-pr-1209.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/datasource/grpc_datasource/execution_plan.go b/app/v2/pkg/engine/datasource/grpc_datasource/execution_plan.go
index 1eb2b6c..159c535 100644
--- a/tmp/agent-patch-flux-pr-1209.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/datasource/grpc_datasource/execution_plan.go
+++ b/app/v2/pkg/engine/datasource/grpc_datasource/execution_plan.go
@@ -126,11 +126,12 @@ func (r RPCFieldSelectionSet) SelectFieldsForTypes(validTypes []string) RPCField
 		}
 
 		for _, field := range lookupFields {
-			if _, found := fieldSet[field.Name]; found {
+			responseKey := field.ResponseKey()
+			if _, found := fieldSet[responseKey]; found {
 				continue
 			}
 
-			fieldSet[field.Name] = struct{}{}
+			fieldSet[responseKey] = struct{}{}
 			fields = append(fields, field)
 		}
 	}
@@ -142,10 +143,7 @@ func (r RPCFieldSelectionSet) SelectFieldsForTypes(validTypes []string) RPCField
 // It contains all information required to extract data from GraphQL variables
 // and construct the appropriate protobuf field.
 type RPCField struct {
-	// Alias can be used to rename the field in the request message
-	// This is needed to make sure that during the json composition,
-	// the field names match the GraphQL request naming.
-	// TODO implement alias handling
+	// Alias stores the GraphQL response alias for the field when one is defined.
 	Alias string
 	// Repeated indicates if the field is a repeated field (array/list)
 	Repeated bool
@@ -192,6 +190,28 @@ func (r RPCFields) Exists(name string) bool {
 	return false
 }
 
+func (r RPCFields) ExistsPath(path string) bool {
+	for _, field := range r {
+		if field.JSONPath == path {
+			return true
+		}
+	}
+
+	return false
+}
+
+func (r RPCField) ResponseKey() string {
+	if r.Alias != "" {
+		return r.Alias
+	}
+
+	if r.JSONPath != "" {
+		return r.JSONPath
+	}
+
+	return r.Name
+}
+
 func (r *RPCExecutionPlan) String() string {
 	var result strings.Builder
 
diff --git a/tmp/agent-patch-flux-pr-1209.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/datasource/grpc_datasource/execution_plan_visitor.go b/app/v2/pkg/engine/datasource/grpc_datasource/execution_plan_visitor.go
index f1ad122..3e0bbb9 100644
--- a/tmp/agent-patch-flux-pr-1209.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/datasource/grpc_datasource/execution_plan_visitor.go
+++ b/app/v2/pkg/engine/datasource/grpc_datasource/execution_plan_visitor.go
@@ -289,6 +289,7 @@ func (r *rpcPlanVisitor) handleRootField(ref int) error {
 // EnterField implements astvisitor.EnterFieldVisitor.
 func (r *rpcPlanVisitor) EnterField(ref int) {
 	fieldName := r.operation.FieldNameString(ref)
+	responsePath := r.operation.FieldAliasOrNameString(ref)
 	if r.IsRootField() {
 		if err := r.handleRootField(ref); err != nil {
 			r.walker.StopWithInternalErr(err)
@@ -311,7 +312,7 @@ func (r *rpcPlanVisitor) EnterField(ref int) {
 	}
 
 	// prevent duplicate fields
-	if r.planInfo.currentResponseMessage.Fields.Exists(fieldName) {
+	if r.planInfo.currentResponseMessage.Fields.ExistsPath(responsePath) {
 		return
 	}
 
@@ -331,9 +332,12 @@ func (r *rpcPlanVisitor) EnterField(ref int) {
 	field := RPCField{
 		Name:     r.resolveFieldMapping(parentTypeName, fieldName),
 		TypeName: typeName.String(),
-		JSONPath: fieldName,
+		JSONPath: responsePath,
 		Repeated: r.definition.TypeIsList(fdt),
 	}
+	if r.operation.FieldAliasIsDefined(ref) {
+		field.Alias = responsePath
+	}
 
 	if typeName == DataTypeEnum {
 		field.EnumName = r.definition.FieldDefinitionTypeNameString(fd)
diff --git a/tmp/agent-patch-flux-pr-1209.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/datasource/grpc_datasource/grpc_datasource_test.go b/app/v2/pkg/engine/datasource/grpc_datasource/grpc_datasource_test.go
index 71bf396..7a4c59f 100644
--- a/tmp/agent-patch-flux-pr-1209.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/datasource/grpc_datasource/grpc_datasource_test.go
+++ b/app/v2/pkg/engine/datasource/grpc_datasource/grpc_datasource_test.go
@@ -316,6 +316,44 @@ func Test_DataSource_Load_WithMockService_WithResponseMapping(t *testing.T) {
 	require.Equal(t, "HARDCODED_NAME_TEST", resp.Data.ComplexFilterType[0].Name)
 }
 
+func Test_DataSource_Load_UsesGraphQLAliasesInResponse(t *testing.T) {
+	conn, cleanup := setupTestGRPCServer(t)
+	t.Cleanup(cleanup)
+
+	query := `query UserAliasQuery($id: ID!) { aliasedUser: user(id: $id) { myId: id myName: name } }`
+	variables := `{"variables":{"id":"1"}}`
+
+	schemaDoc := grpctest.MustGraphQLSchema(t)
+	queryDoc, report := astparser.ParseGraphqlDocumentString(query)
+	require.False(t, report.HasErrors(), report.Error())
+
+	compiler, err := NewProtoCompiler(grpctest.MustProtoSchema(t), nil)
+	require.NoError(t, err)
+
+	ds, err := NewDataSource(conn, DataSourceConfig{
+		Operation:    &queryDoc,
+		Definition:   &schemaDoc,
+		SubgraphName: "Products",
+		Compiler:     compiler,
+		Mapping:      testMapping(),
+	})
+	require.NoError(t, err)
+
+	output := new(bytes.Buffer)
+	err = ds.Load(context.Background(), []byte(`{"query":"`+query+`","body":`+variables+`}`), output)
+	require.NoError(t, err)
+
+	var resp struct {
+		Data map[string]map[string]string `json:"data"`
+	}
+	require.NoError(t, json.Unmarshal(output.Bytes(), &resp))
+	require.Contains(t, resp.Data, "aliasedUser")
+	require.Equal(t, map[string]string{
+		"myId":   "1",
+		"myName": "User 1",
+	}, resp.Data["aliasedUser"])
+}
+
 // Test_DataSource_Load_WithGrpcError tests how the datasource handles gRPC errors
 // and formats them as GraphQL errors in the response
 func Test_DataSource_Load_WithGrpcError(t *testing.T) {
diff --git a/tmp/agent-patch-flux-pr-1209.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/grpctest/schema.go b/app/v2/pkg/grpctest/schema.go
index 0e2a9bf..8f3702a 100644
--- a/tmp/agent-patch-flux-pr-1209.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/grpctest/schema.go
+++ b/app/v2/pkg/grpctest/schema.go
@@ -166,6 +166,16 @@ func GetFieldConfigurations() plan.FieldConfigurations {
 				},
 			},
 		},
+		{
+			TypeName:  "Query",
+			FieldName: "categoriesByKinds",
+			Arguments: []plan.ArgumentConfiguration{
+				{
+					Name:       "kinds",
+					SourceType: plan.FieldArgumentSource,
+				},
+			},
+		},
 		{
 			TypeName:  "Query",
 			FieldName: "filterCategories",
@@ -176,6 +186,16 @@ func GetFieldConfigurations() plan.FieldConfigurations {
 				},
 			},
 		},
+		{
+			TypeName:  "Query",
+			FieldName: "search",
+			Arguments: []plan.ArgumentConfiguration{
+				{
+					Name:       "input",
+					SourceType: plan.FieldArgumentSource,
+				},
+			},
+		},
 		{
 			TypeName:  "Query",
 			FieldName: "calculateTotals",
@@ -196,6 +216,16 @@ func GetFieldConfigurations() plan.FieldConfigurations {
 				},
 			},
 		},
+		{
+			TypeName:  "Mutation",
+			FieldName: "performAction",
+			Arguments: []plan.ArgumentConfiguration{
+				{
+					Name:       "input",
+					SourceType: plan.FieldArgumentSource,
+				},
+			},
+		},
 	}
 }
 
@@ -228,17 +258,22 @@ func GetDataSourceMetadata() *plan.DataSourceMetadata {
 					"typeFilterWithArguments",
 					"typeWithMultipleFilterFields",
 					"complexFilterType",
+					"calculateTotals",
 					"categories",
 					"categoriesByKind",
+					"categoriesByKinds",
 					"filterCategories",
 					"randomPet",
 					"allPets",
+					"search",
+					"randomSearchResult",
 				},
 			},
 			{
 				TypeName: "Mutation",
 				FieldNames: []string{
 					"createUser",
+					"performAction",
 				},
 			},
 		},
@@ -347,6 +382,20 @@ func GetDataSourceMetadata() *plan.DataSourceMetadata {
 					"barkVolume",
 				},
 			},
+			{
+				TypeName: "ActionSuccess",
+				FieldNames: []string{
+					"message",
+					"timestamp",
+				},
+			},
+			{
+				TypeName: "ActionError",
+				FieldNames: []string{
+					"message",
+					"code",
+				},
+			},
 			{
 				TypeName: "UserInput",
 				FieldNames: []string{