Skip to content

refactor(query): extract QueryEntry.supersede — one owner for the reset contract#155

Merged
zzal merged 1 commit into
mainfrom
refactor/query-entry-supersede
Jul 6, 2026
Merged

refactor(query): extract QueryEntry.supersede — one owner for the reset contract#155
zzal merged 1 commit into
mainfrom
refactor/query-entry-supersede

Conversation

@zzal

@zzal zzal commented Jul 6, 2026

Copy link
Copy Markdown
Owner

Part II Wave 1 · #2 — the reset contract had already drifted

The "invalidate this entry's in-flight world" transition (force stale, generation bump, cancel in-flight, retry reset) was written longhand in two places — and they had already diverged:

  • setQueryData cleared entry.error
  • forceStaleAndRefetch did not — despite setQueryData's comment claiming to "mirror" it

So invalidating an entry that held an error kept the stale error visible in snapshots throughout the refetch, while the identical supersede via an optimistic write cleared it. Same sibling-inconsistency shape as Part I #1's diff arms — the dominant defect class per the pre-launch audit.

Change

Both sites now route through one owner, QueryEntry.supersede(clearError:) (QueryEntry.swift), so the transition can't drift apart again. The one real difference between the callers becomes a deliberate, documented, test-pinned decision:

Caller clearError Rationale
forceStaleAndRefetch (invalidate) false No new truth yet — keep the last-known error visible alongside the last-known data (SWR) until the refetch settles and overwrites both. Error persists until proven otherwise (TanStack-aligned).
setQueryData (optimistic write) true The written value IS the new truth; a lingering error would contradict it.

commitFetch's success path shares only the retry-reset half — it is the committing fetch, so there's nothing to bump or cancel — and now calls the extracted resetRetryCycle() (also used inside supersede).

Behavior-preserving, provably

The two policy tests were written and run GREEN against the longhand code first, then the extraction happened under them — they pin today's semantics, not the refactor's. A third test pins supersede's full field-by-field contract (generation bump, in-flight cancel, forced-stale, retry-ladder void, error policy both ways).

Verification

  • New QueryEntry/supersede suite: 3/3
  • SwiflowQuery suite (incl. the state-machine fuzz suite, which hammers invalidate/setQueryData): 123/123
  • Full host suite: 1376 tests, 276 suites green
  • QueryDemo wasm build clean

🤖 Generated with Claude Code

…et contract

The "invalidate this entry's in-flight world" transition (force stale,
generation bump, cancel, retry reset) was written longhand in
forceStaleAndRefetch and setQueryData, and they had already drifted:
setQueryData cleared entry.error while forceStaleAndRefetch did not — despite
setQueryData's comment claiming to mirror it. So invalidating an errored entry
kept the stale error visible through the refetch, while the identical
supersede via an optimistic write cleared it. Same sibling-inconsistency shape
as Part I's diff arms.

Both sites now route through QueryEntry.supersede(clearError:), making the one
real difference a deliberate, documented, test-pinned decision:

- invalidate → clearError: false. No new truth yet — keep the last-known error
  visible alongside the last-known data (SWR) until the refetch settles and
  overwrites both. The error persists until proven otherwise.
- setQueryData → clearError: true. The optimistic write IS the new truth; a
  lingering error would contradict it.

commitFetch's success path shares only the retry-reset half — it IS the
committing fetch, so there is nothing to bump or cancel — and now calls the
extracted resetRetryCycle().

Behavior-preserving: both policy tests were written and run GREEN against the
longhand code before the extraction, so they pin today's semantics, not the
refactor's. A third test pins supersede's full field-by-field contract.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

📦 Bundle size

Artifact Baseline This PR Δ
App.wasm 5.06 MB 5.15 MB +1.91%
App.wasm (gzip) 1.80 MB 1.84 MB +2.20%
JS runtime 54.5 KB 54.5 KB +0.00%
JS runtime (gzip) 11.3 KB 11.3 KB +0.00%
Total (gzip) 1.81 MB 1.85 MB +2.19%

✅ Within budget (≤5% growth allowed).

Baseline: Swift 6.3, WASM SDK 6.3-RELEASE, measured 2026-06-18.

@zzal zzal merged commit ad6bf8c into main Jul 6, 2026
6 checks passed
@zzal zzal deleted the refactor/query-entry-supersede branch July 6, 2026 23:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant