Skip to content

Specification implementation requirements

Updated: 2026-09-27

This document maps issue #18 to GraphQL requirements, implementation gaps, and acceptance cases. It is an implementation target, not evidence of completed conformance. Requirement levels come from the cited specification; project scope, Rust representations, and deviations are identified separately.

The previous agreements about generated output storage structs, receiver-based public APIs, automatic getters, stored/computed field categories, and source synchronization are removed from this conformance plan. They are not prerequisites imposed by GraphQL. UI routing, target audiences, framework comparisons, Context factories, convenience methods, and arbitrary resource defaults are also removed. The requirements below replace those earlier planning constraints; this document does not modify existing Rust code or claim that the separate architecture milestone is complete.

Reference Role and status
GraphQL September 2025 Released baseline required by #18. Its conformance rules define how normative requirements and algorithms are interpreted.
GraphQL Working Draft Next-edition requirements. The selected immutable target is 59bc70ac974b0d5d1e00869f41c06bb3bfc756da; #23 clauses have been compared with the released baseline below. The complete difference inventory remains owned by #28.
GraphQL over HTTP draft Separate HTTP adapter requirements. Candidate source: 3903e68045982cb6710880bf03527aa0e9331667. It is not yet the accepted complete baseline.
GraphQL over WebSocket Selected wire protocol: graphql-transport-ws. An immutable protocol revision remains to be selected.
GraphQL over SSE Selected mode: distinct connections with GET and POST. An immutable protocol revision remains to be selected.
RFC 6455 §4.2.2, HTML EventSource processing and event interpretation Underlying WebSocket handshake and SSE framing requirements; not GraphQL execution rules.

Retain one execution behavior: the selected Working Draft overrides the released baseline where they differ. This is a project baseline decision, not a requirement to support multiple GraphQL modes. Do not follow moving references silently or claim a finalized next edition before its release. Moving links are reading aids; the pinned Working Draft controls acceptance. HTTP/WS/SSE targets remain pending and do not change the #23 default-coercion clauses.

The selected transport scope remains POST queries/mutations, GET queries, subscriptions, modern WebSocket, and distinct-mode SSE. Legacy WebSocket, SSE single connection mode, and Apollo multipart subscriptions are not selected. Core GraphQL does not prescribe a network transport: see subscription delivery agnosticism.

These tables index implementation work; their IDs identify requirements, not GitHub sub-issue numbers. They are not a complete enumeration of every normative clause. Child issues must expand their cited sections into executable checks without treating unlisted mandatory rules as optional.

ID Required behavior Acceptance focus GraphQL reference
G01 Parse source documents and preserve GraphQL names, type references, and literal semantics. Valid/invalid syntax, case-sensitive names, strings, numbers, null, lists, and object literals. §2 Language, §2.10 Input Values
G02 Validate schema definitions and extensions, including root types and input/output type restrictions. Invalid schema rejected before generation/execution; supported extensions and abstract-type relationships. §3 Type System, §3.3 Schema, §3.4.2 Input and Output Types
G03 Implement each built-in scalar’s input and result coercion independently. Int range, Float numeric acceptance, String/Boolean input rejection and permitted result coercion, ID integer/string input and string result. §3.5 Scalars, Int, Float, String, Boolean, ID
G04 Preserve enum membership and the difference between enum literals and variable transport representations. Unknown members, invalid string literals, valid variable values, and invalid resolver results. §3.9 Enums
G05 Support custom scalar input/result coercion and specification metadata. Literal and variable input conversion, output conversion failures, and specifiedByURL introspection. §3.5 Scalars, §3.13.4 @specifiedBy, §4.2.2 __Type
G06 Preserve omitted input entries, explicit null, supplied values, and defaults. Reject unknown input-object fields. Missing versus null with and without defaults; nested input objects; literal/variable parity. §2.10.5 Null Value, §3.10 Input Coercion
G07 Enforce OneOf’s exactly-one, non-null selected entry and its schema/variable restrictions. Zero/multiple entries, null selected value, non-null/defaulted field declarations, nullable variable use. §3.10.1 OneOf Input Objects, §5.8.5 All Variable Usages Are Allowed
G08 Preserve independent list and item nullability and recursive single-value-to-list input coercion. Nested lists, omitted/null containers, null elements, and single-value defaults. §3.11 List, §3.12 Non-Null, §3.12.1 Combining List and Non-Null
G09 Accept legal recursive input types, reject invalid non-null singular cycles, and reject cyclic input-default expansion. Self/mutual recursion, list-broken recursion, terminating defaults, and cycles through lists. §3.10 Circular References, §3.10 Type Validation
G10 Validate executable documents before execution and select the requested operation correctly. Ambiguous/unknown operation name, fragment cycles, field/argument validation, directives, and variable compatibility; no resolver calls on request failure. §5 Validation, §6.1 Executing Requests, §6.1.1 Validating Requests
G11 Coerce variables and field arguments at their specified boundaries, applying applicable defaults. Variable and argument defaults, undefined variables, explicit null, nested defaults; distinguish request errors from argument execution errors. §6.1.2 Coercing Variable Values, §6.4.1 Coercing Field Arguments
ID Required behavior Acceptance focus GraphQL reference
G12 Collect and merge selected fields, honoring aliases, fragments, type conditions, and inclusion directives. Repeated fields, aliases, nested fragments, and @skip/@include; unselected fields are not executed. §5.3.2 Field Selection Merging, §6.3.2 Field Collection, §3.13.1 @skip, §3.13.2 @include
G13 Execute queries normally and top-level mutation fields serially. Observable mutation order; nested mutation selections follow normal completion. No global ordering across independent requests is implied. §6.2.1 Query, §6.2.2 Mutation, §6.3.4 Normal and Serial Execution
G14 Resolve each selected field using its object value and coerced arguments; recursively complete object and list results. Nested objects, per-object argument use, finite selections over recursive output types, and invalid result values. §6.4.2 Value Resolution, §6.4.3 Value Completion
G15 Resolve interface/union results to a valid concrete object type before applying selections. Valid and invalid runtime types, fragment applicability, and __typename. §3.7 Interfaces, §3.8 Unions, Resolving Abstract Types, §4.1 Type Name Introspection
G16 Propagate execution errors through non-null boundaries while retaining valid partial data. Nested/list-item failures, nullable boundaries, root null, and no duplicate error from propagating the same failure. §6.4.4 Handling Execution Errors, §3.11 List
G17 Preserve request-error versus execution-result shapes and error metadata. Request errors omit data; executed results include data; messages, applicable locations, alias/index paths, and extensions. §7.1 Response Format, §7.1.6 Errors, §7.1.7 Extensions
G18 Expose the specified introspection schema and serialize completed responses correctly. Type wrappers, defaults, deprecation, directive metadata, isOneOf, __schema/__type, and response field order. §4 Introspection, §7.2 Serialization Format, §7.2.2 Serialized Map Ordering

Neither G14 nor the GraphQL type system requires a particular Rust struct layout, getter generator, receiver signature, or ownership wrapper. The acceptance target is correct resolution and completion for the selected fields, not an eager Rust copy of the entire reachable schema graph. Reference: §1 Overview and §6.4.2 Value Resolution.

ID Required behavior Acceptance focus GraphQL reference
G19 Validate the subscription root selection and establish its source stream. Single-root-field rule, forbidden root selections/directives, coerced arguments, and source-creation failures. §5.2.4.1 Single Root Field, §6.2.3.1 Source Stream
G20 Execute the selected subscription for each source event and produce response events in order. Event-specific root values; an execution error, including root null, can be followed by another event. §6.2.3.2 Response Stream, §6.4.4 Handling Execution Errors
G21 Distinguish ordinary completion, source failure, and cancellation. Normal exhaustion; exceptional termination; cancellation of the response stream propagates to its source stream. §6.2.3.2 Response Stream, §6.2.3.3 Unsubscribe

Rust mappings directly supporting GraphQL requirements

Section titled “Rust mappings directly supporting GraphQL requirements”

These retained choices implement G03/G05/G06/G07/G08/G09/G15. They are NecrassRs representations, not types or algorithms prescribed by GraphQL.

Use a distinct string-backed Rust type rather than a type alias:

pub struct Id(String);

This accepted representation preserves the distinction between GraphQL ID and String in generated Rust contracts. ID coercion must accept the applicable string/integer inputs and serialize results as strings; a Rust newtype alone does not implement these rules. Preserve supplied string contents without imposing UUID, Base64, or another identifier format. Compose Id with the accepted input-presence and list representations where applicable. Reference: §3.5.5 ID.

Resolve each SDL custom scalar to the fixed consumer path crate::scalars::<mapped SDL name>. Reuse the existing injective identifier mapping in codegen.rs and the naming rules in architecture.md: preserve case, prefix self, Self, super, crate, and names already starting with an underscore with one additional underscore, and emit raw identifiers for other Rust keywords. Do not add a configurable name-to-type mapping.

Examples:

SDL declaration Consumer Rust type path
scalar DateTime crate::scalars::DateTime
scalar type crate::scalars::r#type
scalar self crate::scalars::_self
scalar _self crate::scalars::__self

The consumer owns the custom scalar type and its coercion implementation outside OUT_DIR, for example in src/scalars.rs. The fixed contract is the module path, not a required physical file layout. Generated argument/result contracts and conversion wiring remain in OUT_DIR and reference the consumer type; users do not edit generated files. The generator must not infer a storage type from a scalar’s name or specification URL, or assume all custom scalars wrap String. A consumer may choose pub struct DateTime(String); when appropriate to its scalar definition.

Use one shared scalar trait with three conversion operations: parse_literal converts a GraphQL literal to Self, parse_value converts a variable input value to Self, and serialize converts the scalar value to a response value. Preserve literal kinds until the custom conversion runs; converting every literal to JSON first would erase distinctions such as enum versus string literals. NecrassRs retains responsibility for omission, nullability, GraphQL list wrappers, and contextual error classification and metadata.

Generate the editable scalar implementation scaffold outside OUT_DIR, under the consumer’s scalars module. Users provide the storage representation and fill in the conversion bodies there. Preserve user-defined representation and completed bodies on subsequent generation. The common trait belongs to the runtime library; only the per-scalar implementation scaffold belongs in consumer source. Disposable conversion call sites and generated contracts remain in OUT_DIR. This decision does not prescribe a String placeholder representation or finalize source-synchronization behavior for scalar removal and renaming.

This binding and conversion design supports G05 but is not prescribed by GraphQL. Exact method signatures, public input/output value types, and the conversion error type are deferred to implementation; they do not block further specification planning. Naming a type does not implement its scalar validation. References: §3.5 Scalars, §3.13.4 @specifiedBy, Scalar implementation guide.

Retain the accepted representation:

pub enum GraphQLInput<T> {
Undefined,
Null,
Value(T),
}

It represents a missing entry, a null entry, or a non-null value after applicable coercion/defaulting. A default may turn omission into a value or null; this wrapper does not promise raw request provenance. No separate null-marker type is needed. List elements do not have an omitted-entry state, and output omission is governed by selection rather than this input wrapper.

Use Vec<T> for list containers, with the element representation preserving the SDL item type and its nullability. At an ordinary nullable argument/input-object-field position, compose the presence wrapper outside the list: an input field of type [String!] uses GraphQLInput<Vec<String>>. Do not wrap each list element in GraphQLInput to introduce an undefined item state. The choice of Vec and GraphQLInput is settled; remaining work must implement and verify coercion, defaults, and independent container/item nullability under these representations.

References: §3.10 Input Coercion, §3.11 List, §6.1.2 Variable Coercion, §6.4.1 Argument Coercion, §6.3.2 Field Collection.

Represent a successfully coerced OneOf input object as a Rust enum with one variant per SDL field. Each variant carries the selected field’s non-null value, without a GraphQLInput wrapper on that payload. Preserve nested nullability, including nullable list items. At a nullable argument or ordinary input-object-field position, use GraphQLInput<OneOfType> for omission or null of the entire object. Apply the recursive input layout policy below to recursive variant payloads, using the SDL field edges for SCC analysis.

The enum is an implementation choice, not a substitute for validation. OneOf schema fields must be nullable and have no defaults. Before input coercion, require exactly one supplied entry with a non-null value; after coercion, require exactly one non-null entry. Preserve the specification’s request/execution error distinction, ordinary input-object checks, and non-null variable requirement for variables used as OneOf member values.

Acceptance covers each valid alternative, zero/multiple entries, a null selected value, an unknown field, and a variable used for the selected member. A second supplied entry must be rejected even when its value is null or its variable has no runtime value.

References: §3.10.1 OneOf Input Objects, §3.10 Type Validation, §5.8.5 All Variable Usages Are Allowed.

Retain generation-time SCC analysis of the input-object dependency graph, excluding list-wrapped edges. Box nullable singular field edges inside cyclic components, including self-edges; keep non-null and non-cyclic edges inline. Lists already provide indirection. For ordinary nullable input fields, the box is inside GraphQLInput<Box<T>>. Defaults do not alter the field’s nullability for this analysis.

The justification is that each legal non-list input cycle has a nullable edge; boxing those edges breaks Rust by-value layout cycles. This is a Rust implementation deduction from §3.10 Circular References, not the schema validator itself. Run the type/default validation rules independently. Cover OneOf recursion against the selected draft’s input-object validation.

Do not transfer this nullable-edge rule to output objects: their validity and completion follow §3.6 Objects and §6.4.3 Value Completion.

Represent each interface or union result as a generated Rust enum with one variant carrying each possible concrete Object value. Derive the variants from the validated schema: union member Objects for a union, and possible implementing Objects for an interface, including interface inheritance. Reuse the existing SDL-to-Rust identifier mapping for the enum and variant names.

Generate the enum and its dispatch in OUT_DIR. Use the selected variant to identify the concrete GraphQL Object type, dispatch its field resolvers, apply fragment type conditions, and produce __typename. Consumers return the appropriate variant and Object value. This is an implementation of G15, not a Rust representation mandated by GraphQL. Ownership, indirection, and exact signatures are deferred to implementation; the input-object boxing policy is not automatically applicable here.

Acceptance covers each possible Object type, interface inheritance, applicable and inapplicable fragments, and concrete __typename values. References: §3.7 Interfaces, §3.8 Unions, Resolving Abstract Types, §4.1 Type Name Introspection.

The parent issue requires a local Apollo Compiler patch for input-default-cycle validation and nested variable-default/list coercion. These are implementation responsibilities for G08/G09/G11, not a new GraphQL extension.

Requirement Required work and evidence Reference
Reject cyclic input-default expansion while allowing terminating recursive inputs. Fix dependency-owned validation; exercise self/mutual cycles, including lists, directly against patched Apollo. Input-object Type Validation and InputObjectDefaultValueHasCycle
Apply nested input defaults and list coercion through the public variable-coercion path. Replace wrapper repairs once the dependency fix passes the existing regressions; retain legitimate field-argument processing. Input Coercion, List, Coercing Variable Values, Coercing Field Arguments

Verify that build-time, runtime, and generated consumers actually resolve the patched dependency. Reuse the regressions and dependency-resolution requirements listed in #18; passing the existing wrapper-based tests is not evidence that the dependency is fixed.

Default-cycle correction scope and existing regressions

Section titled “Default-cycle correction scope and existing regressions”

Use the existing recursive-default reproductions as the scope of the schema-validation correction. The required outcome is rejection by Apollo’s schema validator, before generation or request execution, under InputObjectDefaultValueHasCycle. Detecting the problem later in NecrassRs coercion prevents an abort but does not satisfy that schema-validation rule.

The minimal schema already present in request.rs is:

input Recursive {
next: Recursive = {}
}
type Query {
hello(input: Recursive): String
}

Acceptance and test migration:

  • Reject self-cyclic defaults and mutual defaults through lists directly through the patched schema-validation API. Keep subprocess isolation for regressions that could otherwise abort the test runner.
  • Preserve valid recursive types, finite explicitly supplied values, terminating defaults, and reuse of the same finite default across list items and arguments. These are not evidence of a default-expansion cycle.
  • request::tests::cyclic_input_default_is_rejected_without_aborting now requires Apollo schema rejection with diagnostics; later request rejection no longer satisfies the regression.
  • The five cases from execution::tests::cyclic_default_execution_preserves_errors_without_dispatch now live in request::tests::cyclic_argument_defaults_are_rejected_before_execution: literal input, omitted arguments, absent argument variables, absent input-field variables, and a mutual cycle through a list. Each subprocess requires Apollo schema rejection before request preparation or dispatch. The original request documents remain as reproduction context. The previous alias-path/null response is a wrapper fallback, not the target response to an invalid schema.
  • Preserve legitimate argument-error path and null-propagation coverage using valid schemas and applicable execution errors; do not keep an invalid schema valid merely to retain the old assertions. References: Coercing Field Arguments, Handling Execution Errors.
  • Remove superseded active_defaults cycle guards only after the patched dependency is used by build/runtime/consumer paths and direct validation regressions pass. Retain NecrassRs’ required field-argument coercion.

Historical TDD Red checkpoint for #23: cargo test -p necrassrs --locked cyclic_ fails both tests against unpatched Apollo 1.33.0. All six isolated reproductions fail the schema-rejection assertion without aborting. This records the validation gap, not completed conformance. That checkpoint preceded the local patch below; candidate specification revisions and accepted decisions above remain unchanged.

The default-cycle algorithm comparison retains executable path-DFS and dependency-graph candidates, measured results, and the rationale for reusing completed field-default dependency analysis. Its timings remain experimental evidence; the integrated patch uses the selected graph design with iterative traversal.

Nested variable-default normalization is a separate defect within the parent issue’s Apollo deliverable, not implied by fixing schema cycles. Reuse variable_list_default_is_coerced_to_a_list, variable_object_default_applies_input_field_defaults, and nested_list_default_is_coerced_to_a_list from execution.rs, adding direct dependency-level assertions that pass without post-coercion wrapper repair. Its references remain Input Coercion, List Input Coercion, and Coercing Variable Values.

The maintained local Apollo patch now supplies schema default-cycle validation and nested variable-default coercion. Superseded NecrassRs variable normalization and active-default guards are removed. Legitimate executor argument coercion remains. These checks cover the released clauses below and their unchanged counterparts in the selected Working Draft. The #23 reference comparison is recorded below; transport reference selection and the complete parent difference inventory remain separate work. No complete G08/G09/G11 or next-edition conformance is claimed.

Clause / level Applicability and implementation Executable evidence Status
September 2025 §3.10 Type Validation, default-cycle prohibition (required) Apollo field-default graph; distinct from type cycles Existing request cyclic subprocess tests; graph_validation_distinguishes_defaults_from_type_recursion; graph_validation_handles_shared_and_long_default_chains Passed locally; includes diagnostics, extensions, finite recursion and shared/long chains
September 2025 §3.10 Input Coercion and §6.1.2 (required) Apollo variable and nested input-field defaults variable_defaults_are_coerced_by_apollo; supplied_objects_apply_nested_defaults_without_replacing_null_or_values Passed directly through Apollo without wrapper repair
September 2025 §3.11 List Input Coercion (required) Singleton and nested-list defaults in the public variable API The same direct API tests and retained execution default regressions Passed locally
September 2025 §6.4.1 (required; preserved boundary) NecrassRs executor field-argument coercion input_object_defaults_and_single_value_list_coercion_are_applied; finite-default reuse and existing alias/error/null propagation tests Passed after removing only superseded guards
Cargo source selection (project integration requirement) Workspace and external consumer roots, including CLI starter consumer_root_resolves_patched_apollo_for_build_and_runtime; cyclic_defaults_fail_before_consumer_generation; CLI generated-consumer check Passed for local paths; pinned remote Git verification is recorded in the patch record

Validation: cargo test --workspace --locked, workspace all-target Clippy with warnings denied, and formatting pass. The root lockfile resolves Apollo from the local maintained Apollo source. No protocol implementation changes are part of this patch.

The released source is graphql/graphql-spec commit 89d93ebbe05db06787646d76a696ead8de117b2b (the September2025 tag). The selected Working Draft is 59bc70ac974b0d5d1e00869f41c06bb3bfc756da. This fixes the acceptance source; it does not claim complete draft support.

Comparison of the pinned Type System and Execution against the released source found:

  • The default-cycle prohibition and both default-cycle algorithms are unchanged. The field-default dependency graph implements that rule without expanding shared defaults repeatedly. Null and empty lists terminate dependency paths; explicit objects can still require omitted fields’ defaults.
  • Input-object input coercion, list input coercion, and the CoerceVariableValues algorithm are unchanged. The surrounding draft wording clarifies that coercion failures precede operation execution.
  • CoerceArgumentValues is unchanged; its surrounding note clarifies the same execution boundary. Legitimate executor argument coercion remains in place.
  • The new InputObjectHasUnbreakableCycle rule concerns inhabitable input types, including OneOf, and is distinct from default cycles. #28 owns its validation; #24 owns recursive Rust input layout. It is not implemented by this patch.

The checks in the preceding matrix therefore apply to both pinned sources for #23’s clauses. Empty selections and directive changes remain with #28; subscription source creation remains with #29; unique response error paths belong to #27 with #28’s draft inventory. No previously accepted representation or transport behavior is reopened. HTTP/WS/SSE reference selection does not establish or invalidate these core default-coercion checks.

The following topics remain outside #23 and require separate acceptance cases against the pinned Working Draft. Moving links below are reading aids; #28 owns the complete difference inventory.

Topic Reference and required comparison
Empty selection sets §2.5 Selection Sets, §5.3.3 Leaf Field Selections: distinguish explicit empty selections from absent selections.
Directive definitions, extensions, and deprecation §3.13 Directives, §4.2.6 __Directive: validate the added grammar/locations and corresponding introspection.
Uninhabitable input cycles involving OneOf §3.10 Type Validation: reconcile InputObjectHasUnbreakableCycle with the existing default-cycle rule; they are different checks.
Subscription source-creation errors and response error paths §6.2.3.1 Source Stream, §7.1 Response Format, §7.1.6 Errors: preserve the selected revision’s pre-execution/error-position semantics.

These requirements belong to the selected transport specifications, not the GraphQL language specification. Keep protocol selection separate from conformance to that protocol.

The status mappings below were checked against candidate HTTP revision 3903e68045982cb6710880bf03527aa0e9331667. Retain the previously selected pre-execution error mappings and execution-result 200 policy. Selection of the complete immutable HTTP baseline remains pending; that reference task does not reopen these decisions. Distinguish the project’s selected behavior from the source specification’s MUST and SHOULD levels.

ID Requirement or selected scope Reference
H01 Support POST JSON requests; GET query support is selected from the optional methods. GET must not execute a selected mutation; halt with 4xx, with 405 recommended and Allow required when used. §5 Request, §5.3 GET, §5.4 POST
H02 Decode the required string query and optional operationName/variables/extensions with their specified types. JSON encoding is an object; ignore unknown properties. Apply GET empty-parameter and POST optional-null normalization without conflating missing/null entries inside variables. §5.1 GraphQL-over-HTTP Request, §5.3 GET, §5.4.1 JSON Encoding
H03 Support UTF-8 application/json request bodies and application/graphql-response+json responses. Negotiate Accept and label the actual response representation. Retain rejection with 406 when no supported representation is acceptable rather than ignoring Accept. §5.2 Accept, §5.4 POST, §6.1 Body
H04 Preserve GraphQL request-error/execution-result body shapes. Retain 400 for malformed JSON or GraphQL syntax; 422 for invalid envelopes, validation, operation selection, or variable coercion; and 405/415/406 for corresponding method/media failures. These selected mappings follow the candidate’s recommendations without relabeling its SHOULD clauses as MUST. Apply the accepted legacy media-labeling policy below. Candidate Status Codes, §6.1 Body, GraphQL §7.1
H05 Distinguish malformed/oversized transport input from GraphQL execution errors. The candidate recommends 413 for oversized POST bodies and 414 for oversized URIs; it does not prescribe the previous numeric limits. Candidate Status Codes
H06 Ordinary HTTP execution results use 200, including partial errors and root null. This retained project decision departs from the candidate’s 294 SHOULD recommendation. It does not force request failures to use 200. Maintainer decision, Candidate Status Codes, GraphQL §7.1.1 Execution Result

A subscription requires a response stream under GraphQL §6.2.3. When a valid selected subscription permits only a single ordinary JSON response, reject it with 406 before creating its source. Return an errors array without data, using the JSON response media policy below (including its legacy-client recommendation), explaining that subscriptions require SSE or WebSocket. This is a selected application of RFC 9110 §15.5.7, not a subscription status mandated by core GraphQL. If the client also accepts text/event-stream, negotiate an eligible SSE response instead. Do not return only the first event or silently switch to an unacceptable transport.

Follow the HTTP candidate’s SHOULD recommendation for legacy clients: when Accept does not indicate support for a preferred response media type but does accept application/json, process the request as if it accepted application/graphql-response+json. For 2xx responses, label the body application/json; for non-2xx GraphQL-over-HTTP responses, use application/graphql-response+json. This includes the accepted 406 subscription rejection. It supersedes the earlier choice to preserve the client’s JSON media type on every error response; it does not alter the selected HTTP status codes or the execution-result 200 policy. Apply this only to the specified legacy fallback, not as unconditional relabeling of every response. Accepted SSE responses retain text/event-stream.

Acceptance covers Accept: application/json with successful execution, partial/root-null execution results, 400/422 request failures, and 406 subscription rejection; also cover normal negotiation for clients accepting the preferred media type and 406 for clients accepting no supported response type. Reference: Candidate §6.1 Body.

ID Required behavior Reference
W01 Perform the HTTP/1.1 WebSocket handshake, including headers and protocol negotiation; successful upgrade uses 101. The GraphQL subprotocol is graphql-transport-ws. RFC 6455 §4.2.2, Communication
W02 Receive connection_init within the configured wait; timeout uses 4408, repeated init uses 4429. Acknowledge before accepting operations; otherwise use 4401. No particular timeout duration is mandated here. ConnectionInit, ConnectionAck, Subscribe
W03 Validate message structure; invalid messages close with 4400. Duplicate active operation IDs close with 4409. IDs can be reused after completion; distinct operations may interleave. Invalid message, Subscribe, Communication
W04 Respond promptly to protocol ping with pong. Ping, Pong
W05 Deliver results through next and normal completion through complete. A terminal error ends that operation without a following complete. Next, Error, Complete
W06 Honor client completion, suppress subsequent results for that operation, and tolerate completion/in-flight-message races. Cancel sources owned by a closed connection. Complete, GraphQL Unsubscribe

Handshake status and per-operation GraphQL outcomes are different layers. After upgrade, protocol errors use WebSocket messages or close codes, not replacement HTTP statuses. Per-event GraphQL execution errors remain next results; source failure terminates the operation. References: protocol Error and GraphQL Response Stream.

ID Required behavior Reference
S01 Use one operation response stream per distinct-mode request, with selected GET/POST support. Apply the HTTP method restrictions, including rejection of GET mutations. Distinct connections mode, HTTP GET
S02 Accepted EventSource-compatible streams use status 200 and text/event-stream. Encode events according to the SSE parsing rules. Distinct connections mode, EventSource processing, Event interpretation
S03 After transport/envelope acceptance, send GraphQL pre-execution errors on SSE with status 200: one next containing errors without data, then complete. Cover GraphQL syntax, validation, operation selection, variable coercion, and source-creation failures. Applying this consistently to all such failures is the accepted project policy extending the protocol’s explicit pre-execution validation rule. Distinct connections mode, GraphQL Request Error Result, Draft Source Stream
S04 Emit next for results and complete for normal completion. Include an empty data field in complete for EventSource delivery. A single-result operation emits one result; subscriptions produce response events. Distinct-mode event stream, Event interpretation, GraphQL Response Stream
S05 Closing the operation’s SSE connection cancels its stream and source. Distinct connections mode, GraphQL Unsubscribe

HTTP transport/envelope failures retain the selected HTTP statuses: malformed JSON uses 400, invalid request structure uses 422, and method/media failures use their applicable statuses. Do not open an accepted SSE stream for these failures. Use the HTTP error representation policy above, including its legacy JSON fallback, when applicable; when only SSE is acceptable and no ordinary error representation can be sent, retain the failure status with an empty body. This empty-body behavior is a project choice.

For an accepted SSE request, S03 supersedes the ordinary JSON-response mapping for GraphQL pre-execution failures: for example, invalid GraphQL syntax produces a 200 SSE error sequence rather than an ordinary 400 JSON response. Source creation occurs before operation execution under the selected draft direction. A complete event after delivering a request error indicates the response has finished, not that execution succeeded. Include the empty data: field required for EventSource delivery. Verify these boundaries with malformed envelopes, invalid GraphQL documents, invalid variables, and a source factory that returns an error; no field execution may occur after a request failure.

Treat a source-stream failure as terminal for the subscription, distinct from a per-event GraphQL execution error. Core Response Stream requires the response stream to finish with an error when its source does.

Outcome WebSocket Distinct-mode SSE
Per-event GraphQL execution error Send the execution result through next; the subscription can continue. Send the execution result through next; the subscription can continue.
Normal source completion Send complete. Send complete, then end the response normally.
Terminal source failure Send the operation’s error message, with no subsequent complete; retain the socket and unrelated operations. Fail the affected response body without sending complete.

The WebSocket mapping uses the protocol’s Error and Complete messages. The SSE mapping is an accepted NecrassRs transport policy, not an explicit requirement of the SSE protocol. Its event vocabulary provides next and complete but no terminal error event. Do not synthesize an event execution result for a source failure or add a proprietary SSE error event. Record the failure cause on the server. SSE clients cannot obtain structured source-error details through this mapping and may treat the failure as a network interruption and retry according to their own policy.

Acceptance must distinguish body failure from normal end-of-body, verify that no completion event follows a source failure, and verify that unrelated operations remain active. Check actual Axum and Actix protocol exchanges rather than relying solely on an internal stream error. This decision covers failures after source creation; source-creation failures follow S03 instead.

These observations locate work; they are not a fresh exhaustive conformance audit or a claim that existing tests passed during this document revision.

Area Current evidence Requirement coverage to establish
Generated contracts codegen.rs restricts generated argument/result types to String! and generated dispatch to query roots. G03–G09 and G13–G21 through compiling generated consumers.
Execution execution.rs completes String/list/nullability cases, lacks general scalar/object completion, and rejects subscriptions. G03–G05 and G12–G21; reuse existing checks for supported paths.
Defaults/coercion request.rs and execution.rs now use the local Apollo patch for variable defaults and schema default cycles; Cargo.toml overrides Apollo Compiler 1.33.0 with the maintained local source. Executor field-argument coercion remains local. G08/G09/G11 at the dependency boundary and through consumers.
HTTP and streaming shared HTTP, Axum, and Actix need the selected request/response matrix and WS/SSE exchanges. H01–H06, W01–W06, S01–S05; inspect each case before assigning pass/fail.
  1. Complete the pinned Working Draft difference inventory and select immutable HTTP/WS/SSE revisions. The #23 clause comparison above is complete; it is not full parent conformance.
  2. Expand G01–G21 into checks for their normative clauses under the ownership recorded in #18; retain the #23 evidence above.
  3. Establish H01–H06 in both adapters, preserving the documented 200 deviation and accepted 406 subscription rejection. Implement and verify the accepted legacy media-labeling recommendation against the selected transport baseline.
  4. Implement and verify W01–W06 and S01–S05, including the accepted SSE pre-execution error mapping and terminal source-failure policy.
  5. Record each case’s source revision/section, requirement level, applicability, implementation location, executable check, and actual verification status. Use GraphQL Conformance to distinguish mandated behavior from equivalent implementation algorithms.

Generated-consumer compilation and execution, dependency-level regressions, and real protocol exchanges are required evidence under #18. A link, design agreement, snapshot, or successful dependency parse alone is not a passing conformance check. Unspecified product ergonomics are not additional decisions required to complete this matrix.