STET

agent_patch

runs/2026-02-28__13-38-10__gpt-5-1-codex-mini/flux-pr-1297/flux-pr-1297.1-of-1.2026-02-28__13-38-10__gpt-5-1-codex-mini/agent-logs/agent.patch

6473 bytes

Back to adjudication
diff --git a/tmp/agent-patch-flux-pr-1297.1-of-1.2026-02-28__13-38-10__gpt-5-1-codex-mini/app/execution/engine/execution_engine.go b/app/execution/engine/execution_engine.go
index 51b2742..37b16b1 100644
--- a/tmp/agent-patch-flux-pr-1297.1-of-1.2026-02-28__13-38-10__gpt-5-1-codex-mini/app/execution/engine/execution_engine.go
+++ b/app/execution/engine/execution_engine.go
@@ -109,6 +109,12 @@ func WithRequestTraceOptions(options resolve.TraceOptions) ExecutionOptions {
 	}
 }
 
+func WithValidateRequiresDependencies() ExecutionOptions {
+	return func(ctx *internalExecutionContext) {
+		ctx.resolveContext.ExecutionOptions.ValidateRequiresDependencies = true
+	}
+}
+
 func NewExecutionEngine(ctx context.Context, logger abstractlogger.Logger, engineConfig Configuration, resolverOptions resolve.ResolverOptions) (*ExecutionEngine, error) {
 	executionPlanCache, err := lru.New(1024)
 	if err != nil {
diff --git a/tmp/agent-patch-flux-pr-1297.1-of-1.2026-02-28__13-38-10__gpt-5-1-codex-mini/app/v2/pkg/engine/resolve/context.go b/app/v2/pkg/engine/resolve/context.go
index 65d2d6b..1dd3963 100644
--- a/tmp/agent-patch-flux-pr-1297.1-of-1.2026-02-28__13-38-10__gpt-5-1-codex-mini/app/v2/pkg/engine/resolve/context.go
+++ b/app/v2/pkg/engine/resolve/context.go
@@ -38,6 +38,8 @@ type ExecutionOptions struct {
 	SkipLoader                 bool
 	IncludeQueryPlanInResponse bool
 	SendHeartbeat              bool
+	// ValidateRequiresDependencies enables excluding entities with missing @requires dependencies.
+	ValidateRequiresDependencies bool
 }
 
 type FieldValue struct {
diff --git a/tmp/agent-patch-flux-pr-1297.1-of-1.2026-02-28__13-38-10__gpt-5-1-codex-mini/app/v2/pkg/engine/resolve/loader.go b/app/v2/pkg/engine/resolve/loader.go
index 1e9f993..9c87109 100644
--- a/tmp/agent-patch-flux-pr-1297.1-of-1.2026-02-28__13-38-10__gpt-5-1-codex-mini/app/v2/pkg/engine/resolve/loader.go
+++ b/app/v2/pkg/engine/resolve/loader.go
@@ -557,12 +557,23 @@ func (l *Loader) mergeResult(fetchItem *FetchItem, res *result, items []*astjson
 	}
 
 	hasErrors := false
+	var missingRequires *missingRequiresDependenciesResult
 
 	// We check if the subgraph response has errors
 	if res.postProcessing.SelectResponseErrorsPath != nil {
 		errorsValue := value.Get(res.postProcessing.SelectResponseErrorsPath...)
 		if astjson.ValueIsNonNull(errorsValue) {
 			errorObjects := errorsValue.GetArray()
+			missingRequires, err = l.detectMissingRequiresDependencies(fetchItem, errorObjects)
+			if err != nil {
+				return errors.WithStack(err)
+			}
+			if missingRequires != nil && len(missingRequires.extraErrors) > 0 {
+				for _, extra := range missingRequires.extraErrors {
+					astjson.AppendToArray(errorsValue, extra)
+				}
+				errorObjects = errorsValue.GetArray()
+			}
 			hasErrors = len(errorObjects) > 0
 			// If errors field are present in response, but the errors array is empty, we don't consider it as an error
 			// Note: it is not compliant to graphql spec
@@ -614,6 +625,9 @@ func (l *Loader) mergeResult(fetchItem *FetchItem, res *result, items []*astjson
 		return nil
 	}
 	if len(items) == 1 && res.batchStats == nil {
+		if missingRequires != nil && missingRequires.markAll {
+			value = astjson.NullValue
+		}
 		items[0], _, err = astjson.MergeValuesWithPath(items[0], value, res.postProcessing.MergePath...)
 		if err != nil {
 			return errors.WithStack(ErrMergeResult{
@@ -629,6 +643,15 @@ func (l *Loader) mergeResult(fetchItem *FetchItem, res *result, items []*astjson
 		return l.renderErrorsFailedToFetch(fetchItem, res, invalidGraphQLResponseShape)
 	}
 
+	if missingRequires != nil && len(missingRequires.taintedIndexes) > 0 {
+		for idx := range missingRequires.taintedIndexes {
+			if idx < 0 || idx >= len(batch) {
+				continue
+			}
+			batch[idx] = astjson.NullValue
+		}
+	}
+
 	if res.batchStats != nil {
 		uniqueIndexes := res.batchStats.getUniqueIndexes()
 		if uniqueIndexes != len(batch) {
@@ -695,6 +718,96 @@ func (l *Loader) renderErrorsInvalidInput(fetchItem *FetchItem, out *bytes.Buffe
 	return nil
 }
 
+type missingRequiresDependenciesResult struct {
+	taintedIndexes map[int]struct{}
+	extraErrors    []*astjson.Value
+	markAll        bool
+}
+
+func (l *Loader) detectMissingRequiresDependencies(fetchItem *FetchItem, errorObjects []*astjson.Value) (*missingRequiresDependenciesResult, error) {
+	if len(errorObjects) == 0 {
+		return nil, nil
+	}
+	if !l.ctx.ExecutionOptions.ValidateRequiresDependencies {
+		return nil, nil
+	}
+
+	var (
+		_, isEntityFetch      = fetchItem.Fetch.(*EntityFetch)
+		_, isBatchEntityFetch = fetchItem.Fetch.(*BatchEntityFetch)
+	)
+	if !isEntityFetch && !isBatchEntityFetch {
+		return nil, nil
+	}
+
+	tainted := make(map[int]struct{})
+	for _, errValue := range errorObjects {
+		var gqlErr GraphQLError
+		if err := json.Unmarshal(errValue.MarshalTo(nil), &gqlErr); err != nil {
+			continue
+		}
+
+		if len(gqlErr.Path) < 2 {
+			continue
+		}
+		first, ok := gqlErr.Path[0].(string)
+		if !ok || first != "_entities" {
+			continue
+		}
+		if idx, ok := graphQLErrorPathIndex(gqlErr.Path[1]); ok {
+			tainted[idx] = struct{}{}
+		}
+	}
+
+	if len(tainted) == 0 {
+		return nil, nil
+	}
+
+	path := []any{}
+	if fetchItem.ResponsePath != "" {
+		path = []any{fetchItem.ResponsePath}
+	}
+	message := fmt.Sprintf("Missing required dependencies for entity fetch at path '%s'.", fetchItem.ResponsePath)
+	extraErr := GraphQLError{
+		Message: message,
+		Path:    path,
+	}
+	encoded, err := json.Marshal(extraErr)
+	if err != nil {
+		return nil, errors.WithStack(err)
+	}
+	extraValue, err := astjson.ParseWithoutCache(string(encoded))
+	if err != nil {
+		return nil, errors.WithStack(err)
+	}
+
+	return &missingRequiresDependenciesResult{
+		taintedIndexes: tainted,
+		extraErrors:    []*astjson.Value{extraValue},
+		markAll:        isEntityFetch,
+	}, nil
+}
+
+func graphQLErrorPathIndex(value any) (int, bool) {
+	switch idx := value.(type) {
+	case float64:
+		return int(idx), true
+	case int:
+		return idx, true
+	case json.Number:
+		i, err := idx.Int64()
+		if err != nil {
+			return 0, false
+		}
+		return int(i), true
+	case string:
+		if i, err := strconv.Atoi(idx); err == nil {
+			return i, true
+		}
+	}
+	return 0, false
+}
+
 func (l *Loader) appendSubgraphError(res *result, fetchItem *FetchItem, value *astjson.Value, values []*astjson.Value) error {
 	// print them into the buffer to be able to parse them
 	errorsJSON := value.MarshalTo(nil)