agent_patch
runs/2026-02-28__13-38-10__gpt-5-1-codex-mini/flux-pr-1128/flux-pr-1128.1-of-1.2026-02-28__13-38-10__gpt-5-1-codex-mini/agent-logs/agent.patch
9509 bytes
diff --git a/tmp/agent-patch-flux-pr-1128.1-of-1.2026-02-28__13-38-10__gpt-5-1-codex-mini/app/pkg/engine/plan/plan.go b/app/pkg/engine/plan/plan.go
index 4725d5a..4a0d415 100644
--- a/tmp/agent-patch-flux-pr-1128.1-of-1.2026-02-28__13-38-10__gpt-5-1-codex-mini/app/pkg/engine/plan/plan.go
+++ b/app/pkg/engine/plan/plan.go
@@ -246,6 +246,7 @@ func NewPlanner(ctx context.Context, config Configuration) *Planner {
planningVisitor := &Visitor{
Walker: &planningWalker,
fieldConfigs: map[int]*FieldConfiguration{},
+ operationDefinition: -1,
disableResolveFieldPositions: config.DisableResolveFieldPositions,
}
@@ -449,6 +450,36 @@ func (v *Visitor) currentFullPath() string {
return path
}
+func (v *Visitor) isRootField(ref int) bool {
+ return strings.Count(v.currentFullPath(), ".") == 1
+}
+
+func (v *Visitor) rootOperationTypeName() []byte {
+ if v.operationDefinition < 0 || v.operationDefinition >= len(v.Operation.OperationDefinitions) {
+ return nil
+ }
+ operationType := v.Operation.OperationDefinitions[v.operationDefinition].OperationType
+ switch operationType {
+ case ast.OperationTypeQuery:
+ if len(v.Definition.Index.QueryTypeName) != 0 {
+ return v.Definition.Index.QueryTypeName
+ }
+ return ast.DefaultQueryTypeName
+ case ast.OperationTypeMutation:
+ if len(v.Definition.Index.MutationTypeName) != 0 {
+ return v.Definition.Index.MutationTypeName
+ }
+ return ast.DefaultMutationTypeName
+ case ast.OperationTypeSubscription:
+ if len(v.Definition.Index.SubscriptionTypeName) != 0 {
+ return v.Definition.Index.SubscriptionTypeName
+ }
+ return ast.DefaultSubscriptionTypeName
+ default:
+ return nil
+ }
+}
+
func (v *Visitor) EnterDirective(ref int) {
directiveName := v.Operation.DirectiveNameString(ref)
ancestor := v.Walker.Ancestors[len(v.Walker.Ancestors)-1]
@@ -527,13 +558,22 @@ func (v *Visitor) EnterField(ref int) {
fieldName := v.Operation.FieldNameBytes(ref)
fieldAliasOrName := v.Operation.FieldAliasOrNameBytes(ref)
if bytes.Equal(fieldName, literal.TYPENAME) {
+ stringNode := &resolve.String{
+ Nullable: false,
+ IsTypeName: true,
+ }
+ if v.isRootField(ref) {
+ typeName := v.rootOperationTypeName()
+ if typeName == nil {
+ typeName = []byte{}
+ }
+ stringNode.StaticValue = append([]byte{}, typeName...)
+ } else {
+ stringNode.Path = []string{"__typename"}
+ }
v.currentField = &resolve.Field{
- Name: fieldAliasOrName,
- Value: &resolve.String{
- Nullable: false,
- Path: []string{"__typename"},
- IsTypeName: true,
- },
+ Name: fieldAliasOrName,
+ Value: stringNode,
OnTypeNames: v.resolveOnTypeNames(),
Position: v.resolveFieldPosition(ref),
SkipDirectiveDefined: skip,
diff --git a/tmp/agent-patch-flux-pr-1128.1-of-1.2026-02-28__13-38-10__gpt-5-1-codex-mini/app/pkg/engine/plan/plan_test.go b/app/pkg/engine/plan/plan_test.go
index d57cad2..8889d32 100644
--- a/tmp/agent-patch-flux-pr-1128.1-of-1.2026-02-28__13-38-10__gpt-5-1-codex-mini/app/pkg/engine/plan/plan_test.go
+++ b/app/pkg/engine/plan/plan_test.go
@@ -208,6 +208,28 @@ func TestPlanner_Plan(t *testing.T) {
DefaultFlushIntervalMillis: 0,
}))
+ t.Run("root typename resolved statically", test(rootTypenameDefinition, `
+ query RootTypeName {
+ __typename
+ }
+ `, "RootTypeName", &SynchronousResponsePlan{
+ Response: &resolve.GraphQLResponse{
+ Data: &resolve.Object{
+ Fields: []*resolve.Field{
+ {
+ Name: []byte("__typename"),
+ Value: &resolve.String{
+ StaticValue: []byte("Query"),
+ IsTypeName: true,
+ },
+ },
+ },
+ },
+ },
+ }, Configuration{
+ DisableResolveFieldPositions: true,
+ }))
+
t.Run("Union response type with interface fragments", test(testDefinition, `
query SearchResults {
searchResults {
@@ -645,6 +667,17 @@ var expectedMyHeroPlanWithFragment = &SynchronousResponsePlan{
},
}
+const rootTypenameDefinition = `
+schema {
+ query: Query
+}
+
+type Query {
+ dummy: String
+}
+
+`
+
const testDefinition = `
directive @defer on FIELD
diff --git a/tmp/agent-patch-flux-pr-1128.1-of-1.2026-02-28__13-38-10__gpt-5-1-codex-mini/app/pkg/engine/resolve/resolve.go b/app/pkg/engine/resolve/resolve.go
index f6b93a0..ecf3bb0 100644
--- a/tmp/agent-patch-flux-pr-1128.1-of-1.2026-02-28__13-38-10__gpt-5-1-codex-mini/app/pkg/engine/resolve/resolve.go
+++ b/app/pkg/engine/resolve/resolve.go
@@ -1014,44 +1014,49 @@ func (r *Resolver) resolveString(ctx *Context, str *String, data []byte, stringB
err error
)
- value, valueType, _, err = jsonparser.Get(data, str.Path...)
- if err != nil || valueType != jsonparser.String {
- if err == nil && str.UnescapeResponseJson {
- switch valueType {
- case jsonparser.Object, jsonparser.Array, jsonparser.Boolean, jsonparser.Number, jsonparser.Null:
- stringBuf.Data.WriteBytes(value)
- return nil
+ if str.StaticValue != nil {
+ value = str.StaticValue
+ } else {
+
+ value, valueType, _, err = jsonparser.Get(data, str.Path...)
+ if err != nil || valueType != jsonparser.String {
+ if err == nil && str.UnescapeResponseJson {
+ switch valueType {
+ case jsonparser.Object, jsonparser.Array, jsonparser.Boolean, jsonparser.Number, jsonparser.Null:
+ stringBuf.Data.WriteBytes(value)
+ return nil
+ }
}
+ if value != nil && valueType != jsonparser.Null {
+ return fmt.Errorf("invalid value type '%s' for path %s, expecting string, got: %v. You can fix this by configuring this field as Int/Float/JSON Scalar", valueType, string(ctx.path()), string(value))
+ }
+ if !str.Nullable {
+ return errNonNullableFieldValueIsNull
+ }
+ r.resolveNull(stringBuf.Data)
+ return nil
}
- if value != nil && valueType != jsonparser.Null {
- return fmt.Errorf("invalid value type '%s' for path %s, expecting string, got: %v. You can fix this by configuring this field as Int/Float/JSON Scalar", valueType, string(ctx.path()), string(value))
- }
- if !str.Nullable {
+
+ if value == nil && !str.Nullable {
return errNonNullableFieldValueIsNull
}
- r.resolveNull(stringBuf.Data)
- return nil
- }
- if value == nil && !str.Nullable {
- return errNonNullableFieldValueIsNull
- }
+ if str.UnescapeResponseJson {
+ value = bytes.ReplaceAll(value, []byte(`\"`), []byte(`"`))
- if str.UnescapeResponseJson {
- value = bytes.ReplaceAll(value, []byte(`\"`), []byte(`"`))
+ // Do not modify values which was strings
+ // When the original value from upstream response was a plain string value `"hello"`, `"true"`, `"1"`, `"2.0"`,
+ // after getting it via jsonparser.Get we will get unquoted values `hello`, `true`, `1`, `2.0`
+ // which is not string anymore, so we need to quote it again
+ if !(bytes.ContainsAny(value, `{}[]`) && gjson.ValidBytes(value)) {
+ // wrap value in quotes to make it valid json
+ value = append(literal.QUOTE, append(value, literal.QUOTE...)...)
+ }
- // Do not modify values which was strings
- // When the original value from upstream response was a plain string value `"hello"`, `"true"`, `"1"`, `"2.0"`,
- // after getting it via jsonparser.Get we will get unquoted values `hello`, `true`, `1`, `2.0`
- // which is not string anymore, so we need to quote it again
- if !(bytes.ContainsAny(value, `{}[]`) && gjson.ValidBytes(value)) {
- // wrap value in quotes to make it valid json
- value = append(literal.QUOTE, append(value, literal.QUOTE...)...)
+ stringBuf.Data.WriteBytes(value)
+ r.exportField(ctx, str.Export, value)
+ return nil
}
-
- stringBuf.Data.WriteBytes(value)
- r.exportField(ctx, str.Export, value)
- return nil
}
value = r.renameTypeName(ctx, str, value)
@@ -1541,6 +1546,7 @@ type FieldExport struct {
type String struct {
Path []string
Nullable bool
+ StaticValue []byte `json:"static_value,omitempty"`
Export *FieldExport `json:"export,omitempty"`
UnescapeResponseJson bool `json:"unescape_response_json,omitempty"`
IsTypeName bool `json:"is_type_name,omitempty"`
diff --git a/tmp/agent-patch-flux-pr-1128.1-of-1.2026-02-28__13-38-10__gpt-5-1-codex-mini/app/pkg/engine/resolve/resolve_test.go b/app/pkg/engine/resolve/resolve_test.go
index fbc4a6a..2b7b245 100644
--- a/tmp/agent-patch-flux-pr-1128.1-of-1.2026-02-28__13-38-10__gpt-5-1-codex-mini/app/pkg/engine/resolve/resolve_test.go
+++ b/app/pkg/engine/resolve/resolve_test.go
@@ -2253,6 +2253,21 @@ func TestResolver_ResolveGraphQLResponse(t *testing.T) {
},
}, `{"data":{"user":{"id":1,"name":"Jannik","__typename":"namespaced_User","aliased":"namespaced_User","rewritten":"namespaced_User"}}}`
}))
+ t.Run("root typename static response", testFn(false, false, func(t *testing.T, ctrl *gomock.Controller) (*GraphQLResponse, Context, string) {
+ return &GraphQLResponse{
+ Data: &Object{
+ Fields: []*Field{
+ {
+ Name: []byte("__typename"),
+ Value: &String{
+ StaticValue: []byte("Query"),
+ IsTypeName: true,
+ },
+ },
+ },
+ },
+ }, Context{ctx: context.Background()}, `{"data":{"__typename":"Query"}}`
+ }))
t.Run("empty graphql response for not nullable query field", testFn(false, false, func(t *testing.T, ctrl *gomock.Controller) (node *GraphQLResponse, ctx Context, expectedOutput string) {
return &GraphQLResponse{
Data: &Object{