diff --git a/gaps/GAP-10/DRAFT.md b/gaps/GAP-10/DRAFT.md
index 76ae88f..ba5359f 100644
--- a/gaps/GAP-10/DRAFT.md
+++ b/gaps/GAP-10/DRAFT.md
@@ -4,15 +4,15 @@ This document specifies the `@mock` directive, which allows GraphQL clients to
return mocked data for individual fields, selection sets, or entire operations.
Mock data may be defined for fields and types that do not yet exist in the
-schema. This enables backend and client developers to work in parallel — client
-developers can start building applications using expected new fields without
-waiting for the server to implement the new schema.
+deployed schema. This enables backend and client developers to work in parallel
+— client developers can start building applications using expected new fields
+without waiting for the server to implement the new schema.
```graphql example
query GetBusinessInfo {
business(id: "123") {
name
- # this field doesn't exist yet on the server!
+ # this field is defined locally but not yet deployed on the server!
website @mock(value: "https://www.example.com")
}
}
@@ -130,6 +130,13 @@ that {SelectionSet} must be removed:
- If the parent is a fragment definition, remove the definition and all of its
corresponding fragment spreads.
+Removal must be applied recursively up the tree: if pruning a field or fragment
+leaves its parent with an empty selection set, that parent must also be removed.
+
+If recursive removal would eliminate the operation's entire root selection set,
+this is a validation error. In this case, `@mock` should be applied to the
+operation itself rather than to its individual fields.
+
After removing mocked selections, if an operation
[variable](https://spec.graphql.org/September2025/#sec-Language.Variables) is no
longer referenced by any remaining selection, the variable definition must also
@@ -144,7 +151,7 @@ particular, `@mock` takes precedence over `@skip` and `@include` — the mock da
is always returned, irrespective of whether the field would otherwise be skipped
or included based on those directives' conditions.
-If `@mock` is applied to an operation definition (e.g. {"query"}), the entire
+If `@mock` is applied to an operation definition (e.g., {"query"}), the entire
response must be resolved from a _mock file_. No request should be sent to the
server.
@@ -169,7 +176,9 @@ query GetFoo($id: ID!, $planet: String!) {
...FooFields
...MoreFooFields
... on Foo { baz @mock(value: "baz!") }
- sayHello(planet: $planet) @mock(value: "hello world")
+ greetings {
+ sayHello(planet: $planet) @mock(value: "hello world")
+ }
}
}
```
@@ -215,7 +224,7 @@ TransformOperation(document, selectionSet) :
When `@mock` is applied to a selection which is a child of a list type, the
client inserts the same _mock value_ in each corresponding array element in
-the repsonse.
+the response.
For example, this query inserts the same {"blurHash"} value for each item in
{"menuItems"}:
@@ -244,7 +253,7 @@ require a *mock file*.
Note: Mock files are intended to be long-lived and may be checked into version
control. This is useful for client developers working on a project over an
extended period of time, and where the client code depends on GraphQL schema
-that does not yet exist.
+that is defined locally but not yet deployed on the server.
## Mock File Location
@@ -335,7 +344,8 @@ list value directly:
[GraphQL error format](https://spec.graphql.org/September2025/#sec-Errors).
This is valid for both operation-level and field-level `@mock` directives.
-The client must merge {"errors"} into the GraphQL server's response if present.
+The client must append the contents of {"errors"} to the response's {"errors"}
+array.
#### extensions
@@ -373,10 +383,10 @@ is or may be applied for a given *mock variant id*.
:: A *field path* is a dot-separated string of field names (or aliases, where
present) representing the location of the field relative to the root of the
-operation or fragment.
+operation or fragment.
For `@mock` on an operation root, {"__path__"} is the root operation type name
-(e.g. {"Query"}, {"Mutation"}, or {"Subscription"}).
+(e.g., {"Query"}, {"Mutation"}, or {"Subscription"}).
**Example**
@@ -431,8 +441,8 @@ This would be a (minimally) valid corresponding *mock file*:
#### __metadata__
-{"__metadata__"} may be a key-value mapping for additional user or application
-defined metadata.
+{"__metadata__"} may be a key-value mapping for additional user or
+application-defined metadata.
# Validation
@@ -443,6 +453,10 @@ may become invalid over time.
Conforming clients must verify that mock data is valid for each operation.
+Note: When validation occurs is implementation-defined. Common strategies
+include validating at code generation time, as part of a test suite, or
+on-demand when an operation is modified.
+
## Mock File Validation
If a *mock variant id* referenced by a {"variant"} argument does not exist in
@@ -459,16 +473,16 @@ a *mock file*.
A *mock value* is valid when its shape is compatible with the operation's
selections at the *field path* where `@mock` is applied. For each selected
-field, the *mock value* must satisfy {CompleteValue()} for the field's
-schema type. Fields present in the operation but not defined in the
-schema are skipped during validation.
+field, the *mock value* must satisfy {CompleteValue()} for the field's type
+as defined in the client's local schema. The local schema may include types
+and fields not yet deployed on the server.
-Note: It is also possible to detect if a JSON payload is valid for a given
+Note: It is possible to detect if a JSON payload is valid for a given
operation by constructing an in-memory GraphQL server that has no resolvers and
uses the JSON payload as its {rootValue} — then ensuring no errors are
thrown for execution of the operation against the test server. The schema
must be modified to include any new types and fields referenced in the
-*mock value*.
+*mock value* to use this method.
## Inline Mock Value Validation
@@ -530,7 +544,7 @@ ValidateNoNestedMocks(selectionSet, isMockedByParent) :
* Let {fieldUsesMock} be {true} if {selection} has a `@mock` directive,
otherwise {false}.
* If {isMockedByParent} is {true}, {fieldUsesMock} must be {false}.
- * Let {isChildrenMocked} be {true} if both {isMockedByParent} and
+ * Let {isChildrenMocked} be {true} if {isMockedByParent} or
{fieldUsesMock} is {true}, otherwise {false}.
* If {selection} has a {selectionSet}:
* Let {nextSelectionSet} be that {selectionSet}.
@@ -543,6 +557,10 @@ that does not use `@mock`. If every selection in the operation's root
{SelectionSet} is mocked, the transformed operation would contain an
empty {SelectionSet}, which is not valid GraphQL.
+Note: This rule does not apply when `@mock` is applied to the operation
+definition itself — in that case, no request is sent to the server and the
+entire response is resolved from the *mock file*.
+
```graphql counter-example
# ❌ Validation error: all top-level fields are mocked
query GetBusiness {
diff --git a/gaps/GAP-10/README.md b/gaps/GAP-10/README.md
index bfc0023..10f1f0c 100644
--- a/gaps/GAP-10/README.md
+++ b/gaps/GAP-10/README.md
@@ -9,13 +9,14 @@ JSON files alongside the operations that use them.
## Motivation
Client and backend developers often work in parallel, but clients cannot build
-against schema that isn't yet implemented. The `@mock` directive lets client
-developers define and use mock responses for fields and types that may not yet
-be present in the server schema, unblocking frontend development.
+against schema that isn't yet deployed. The `@mock` directive lets client
+developers define and use mock responses for fields and types that are defined
+locally but not yet present in the server schema, unblocking frontend
+development.
## FAQs
-### Conditional response based on list position
+### Can I use different mock values for different list positions?
This specification does not support conditional use of mock values in individual array
positions. You may, however, hoist usage of `@mock` to the parent node that returns
@@ -59,169 +60,35 @@ query PetStorePets {
}
```
-#### Future Proposal
-
-We may in future extend this specification to allow for a random pick of
-multiple mock values:
-
-```graphql
-query PetStorePets {
- dogsForSale {
- name @mock(value: "john") @mock(value: "ringo") @mock(value: "paul")
- }
-}
-```
-
-This may produce the following response:
-
-```json
-{
- "data": {
- "dogsForSale": [
- { "name": "paul" },
- { "name": "ringo" },
- { "name": "paul" }
- ]
- }
-}
-```
-
-#### Why don't we allow specifying an array position?
+#### Why isn't array position supported?
+Details
-Consider the following query:
-
-```graphql
-query GetUserFavorites {
- businessesNearMe {
- menuItems {
- name
- price
- }
- }
-}
-```
-
-Consider that we wish to mock a new field that does not yet exist on the server;
-`blurHash`:
-
-```graphql
-query GetUserFavorites {
- businessesNearMe {
- menuItems {
- name
- price
- photo {
- url
- blurHash @mock(value: "LEHV6nWB2yk8pyo0adR*.7kCMdnj")
- }
- }
- }
-}
-```
-
-Currently, all list items in the user interface will use the same string for
-`blurHash` - we have no way to use different values for different positions in
-the list.
-
-A tempting option might be to add a "list position argument" that the client
-can interpret to merge in the mock value in the right position:
-
-```graphql
-query GetUserFavorites {
- businessesNearMe {
- menuItems {
- name
- price
- photo {
- url
- blurHash
- @mock(value: "LEHV6nWB2yk8pyo0adR*.7kCMdnj", nth_child: 0)
- @mock(value: "L6PZfSi_.AyE_3t7t7R**0o#DgR4", nth_child: 1)
- }
- }
- }
-}
-```
-
-_(Fans of CSS will find
-[`nth_child`](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/:nth-child)
-familiar.)_
-
-However, there are issues with this API:
-
-##### 1. Parent Ambiguity
-
-Which list parent does `nth-child` apply to? There are two list parents of
-`Photo.blurHash`: (1) `Query.businessesNearMe` and (2) `Business.menuItems`.
-
-We would need an even more complicated API to resolve this ambiguity.
-
-##### 2. Order Instability
-
-Results from the server may not be stable over long periods of time.
-
-Consider this example that searches book stores for available books:
+A hypothetical `nth_child` argument (e.g., `@mock(value: "...", nth_child: 0)`)
+would let clients target specific array positions. However, this introduces
+several problems:
-```graphql
-query BooksInStock {
- findBookInStock(name: "Fly Fishing", author: "JR Hartley") {
- inStock
- storeDetails { name address } @mock(..., nth_child: 0)
- }
-}
-```
-
-The data the server returns may change over time, either due to:
+1. **Parent Ambiguity** — A mocked field may be nested under multiple list
+ parents (e.g., `businessesNearMe[].menuItems[].blurHash`). It's unclear
+ which list `nth_child` indexes into.
-1. Organic results change
-2. The server changes behavior (e.g. a default `orderBy` parameter changes from
- `ASC to `DESC` -- this should not happen by convention, but we cannot rely on
- this.)
+2. **Order Instability** — Server results may change order over time (e.g., a
+ default sort changes), causing mock data to be merged into the wrong object
+ and producing broken UI states.
-This means that the result object that the mocked field is merged into may
-change over time - resulting in a different UI state than was intended,
-potentially leading to nonsensical or broken UI states.
+3. **Abstract types** — When a list contains a union or interface, it's
+ ambiguous whether `nth_child: 3` means "the third element overall" or "the
+ third element of a specific concrete type." This is further complicated when
+ the mocked field lives in a fragment that may be spread into both list and
+ non-list contexts.
-##### 3. Abstract types
-
-```graphql
-query BooksInStock {
- findBookInStock(name: "Fly Fishing", author: "JR Hartley") {
- inStock
- ... on PhysicalStore {
- storeDetails { name address } @mock(..., nth_child: 3)
- }
- ... on Website {
- websiteDetails { name url } @mock(..., nth_child: 3)
- }
- }
-}
-```
-
-It is ambiguous if the `nth_child` applies to the concrete or abstract type.
-i.e. does `nth_child: 3` means "the third occurrence of PhysicalStore
-specifically" or "the third element of `findBookInStock`"?
-
-This is particularly problematic where applied inside a fragment defined in a
-separate file, and it's not obvious that the fragment is even called inside of
-a list!
-
-And what if that fragment is reused in multiple queries, both as a list and not
-as a list? 🤯
-
-##### Summary
-
-Although complex, the issues above are solvable in theory. However given the
-complexity, the current version of this specification does not support this.
-
-If you have ideas for how to solve this, please send a pull request for a future
-spec version :)
+These issues are solvable in theory but add significant complexity. This version
+of the specification does not support positional mocking.
-### Mocking on inline fragments and fragment spreads
+### Why isn't @mock supported on inline fragments or fragment spreads?
`@mock` is not supported on inline fragments or fragment spreads:
@@ -262,37 +129,6 @@ query Foo {
Why not?
-#### New types are unreachable
-
-The primary motivation for `@mock` is building against schema that doesn't yet
-exist. But when a type condition references a new type, the server will never
-return that type in its response — so the mock data has nowhere to be merged.
-
-```graphql
-query PetStore {
- forSale {
- ... on Dog {
- breed
- }
- # Fish doesn't exist in the schema yet.
- # The server will never return __typename: "Fish",
- # so this mock data can never be inserted.
- ... on Fish @mock(variant: "clownfish") {
- species
- }
- }
-}
-```
-
-For the non-list case, the client could force-overlay the mock data, but this
-means overriding the server's type resolution — semantically different from what
-`@mock` does elsewhere, where it merges data into a known position in the
-response tree.
-
-For the list case, the client would need to *inject* new objects into the array,
-raising the same position-ambiguity problems described in
-[Why don't we allow specifying an array position?](#why-dont-we-allow-specifying-an-array-position).
-
#### `__typename` dependency
Merging mock data for a type condition requires knowing which type the server
diff --git a/gaps/GAP-10/SKILL.md b/gaps/GAP-10/SKILL.md
index f0db8cd..9ec8eb2 100644
--- a/gaps/GAP-10/SKILL.md
+++ b/gaps/GAP-10/SKILL.md
@@ -26,7 +26,7 @@ A "mock variant" is an object containing the following attributes:
- **`data`** (required): The mock value - the raw data to be returned - defined by https://spec.graphql.org/September2025/#sec-Data.
- **`errors`**: May contain an errors array - defined by https://spec.graphql.org/September2025/#sec-Errors.
- **`extensions`**: May contain arbitrary data - defined by https://spec.graphql.org/September2025/#sec-Extensions.
-- **`__path__`** (required): The field path within the operation or fragment where `@mock` is applied. A dot-separated string of field names (or aliases, where present) relative to the root. For `@mock` on an operation root, use the root operation type name (e.g. `"Query"`, `"Mutation"`, `"Subscription"`). When a field has an alias, use the alias in the path.
+- **`__path__`** (required): The field path within the operation or fragment where `@mock` is applied. A dot-separated string of field names (or aliases, where present) relative to the root. For `@mock` on an operation root, use the root operation type name (e.g., `"Query"`, `"Mutation"`, `"Subscription"`). When a field has an alias, use the alias in the path.
- **`__description__`**: A natural language description of the data being returned.
- **`__metadata__`**: May contain a key/value mapping of arbitrary data.
@@ -61,7 +61,7 @@ A "mock variant" is an object containing the following attributes:
When generating a new mock value, add the following to your context window:
-- the user's prompt (e.g. "add a @mock response for this field ")
+- the user's prompt (e.g., "add a @mock response for this field ")
- the corresponding selection and nested selection set in the operation or fragment
- the GraphQL schema. It is possible that some of the fields in the mock
response already exist in the schema, and may be looked up - you can use the
@@ -75,17 +75,13 @@ otherwise).
Ensure the generated mock value is valid against the selections in the operation
or fragment.
-Use plausible and realistic values. e.g. for "Business.name", use a made-up
+Use plausible and realistic values, e.g., for "Business.name", use a made-up
business name such as "The Great British Bakery". Avoid using "foo", "bar",
"myBusiness", "string" etc. as values.
Generate a __description__ field which summarizes the output, and has enough
context such that it could be used to regenerate a similarly shaped payload.
-The values generated for leaf nodes do not matter and do not need to be
-preserved or included in the description - unless otherwise specified by the
-user.
-
### Errors
When the mock should represent an error state, use the GraphQL errors format -
@@ -96,11 +92,14 @@ check against the schema.
```json
{
- "data": { "fieldName": null },
- "errors": [{
- "path": ["fieldName"],
- "message": "field error"
- }]
+ "field-error": {
+ "data": { "fieldName": null },
+ "errors": [{
+ "path": ["fieldName"],
+ "message": "field error"
+ }],
+ "__path__": "fieldName"
+ }
}
```
@@ -110,7 +109,8 @@ When asked to add or update a mock variant:
1. Locate the operation or fragment's mock file
2. Read the existing mock file to understand the mock value shape
-3. If creating a new mock, create a new entry with a descriptive mock variant id
+3. If creating a new mock, create a new entry with a descriptive mock variant ID
+ (must NOT start with two underscores `__`)
4. Ensure the mock value includes appropriate `data` and/or `errors` fields
5. Add a summary of the user's prompt to the `__description__` field
6. Write the updated mock file (do not modify other mocks in the file)
@@ -118,5 +118,5 @@ When asked to add or update a mock variant:
## Schema
Look for the GraphQL schema in the repository to understand what shape of data
-should be returned. (e.g. /schema.graphql). Ask the user if the
-schema file cannot be found, and remember where it is located for future.
+should be returned. (e.g., `/schema.graphqls`). Ask the user if the
+schema file cannot be found, and remember where it is located.