June 15, 2026 · 3 min read
A measured Codex run fixing a persisted TanStack Query mutation by finding the default mutation function rule.
The fixture is a React checkout flow using TanStack Query persistence.
The user places an order while offline. TanStack Query s the mutation. The
app persists the query cache. After reload, the app restores the cache, goes
online, and calls resumedMutations()
.
Expected result: the order is submitted once after reload.
Actual result: the order is never submitted.
Both runs used Codex GPT-5.5 against the same fixture. The prompt was:
Fix this TypeScript React fixture so `npm test` and `npm run typecheck` succeed, preserving queued checkout writes across app reloads.
The target packages were @tanstack/react-query 5.101.0
and @tanstack/react-query-persist-client 5.101.0
.
Case study replay
TanStack Query persisted checkout outbox #
model Codex GPT-5.5Fix this TypeScript React fixture so npm test and npm run typecheck succeed, preserving queued checkout writes across app reloads.
Without GitHits
-
tokens
-
0
-
time
-
0s / 126s
-
Ready. Click "Watch Replay" to start.
-
Reached the same mutation-default fix, but first installed dependencies and reconstructed the persistence path from local TanStack Query internals.
With GitHits
-
tokens
-
0
-
time
-
0s / 79s
-
Ready. Click "Watch Replay" to start.
-
Used TanStack Query persistence docs to register a keyed mutation default, allowing hydrated d checkout writes to resume after reload.
Result #
| Run | Time | Tokens | Tools |
|---|---|---|---|
| With GitHits | 79s | 350,965 | 19 |
| Without GitHits | 126s | 1,031,661 | 35 |
Both runs produced a passing patch. The GitHits run used 680,696 fewer processed tokens and 16 fewer tool calls.
Failure #
The fixture created a QueryClient
without mutation defaults:
export function createCheckoutClient(_api?: CheckoutApi): QueryClient {
return new QueryClient({
defaultOptions: {
queries: { retry: false },
mutations: { retry: false },
},
});
}
The checkout component passed the mutation function to useMutation
:
const submitOrder = useMutation({
mutationKey: submitOrderMutationKey,
mutationFn: api.submitOrder,
});
That function exists only in the mounted component. After persistence and reload, the restored mutation has its key and state. It does not have the component closure.
The relevant TanStack Query rule: a persisted d mutation needs a default mutation function registered for its mutation key before it can resume.
Fix #
Register the checkout submit function on the client:
export function createCheckoutClient(api?: CheckoutApi): QueryClient {
const client = new QueryClient({
defaultOptions: {
queries: { retry: false },
mutations: { retry: false },
},
});
if (api) {
client.setMutationDefaults(submitOrderMutationKey, {
mutationFn: api.submitOrder,
});
}
return client;
}
The test restores an offline write into a fresh client, goes online, resumes d mutations, and checks that the checkout API receives the original order once.
Trace #
The GitHits run found the rule with docs search:
search target=npm:@tanstack/react-query@5.101.0 source=docs query=persistQueryClient resumedMutations mutation defaults offline persistence reload limit=5
docs_read page_id=20025 start_line=1 end_line=120
docs_read page_id=20025 start_line=340 end_line=416
It then patched createCheckoutClient
and ran:
npm test
npm run typecheck
The no-GitHits run installed dependencies and searched local package source for the same behavior. It read TanStack Query hydration, mutation cache, query client defaults, and default mutation option code before editing.
In this run, docs search found the package contract directly. The agent did not need to inspect source internals to discover it.