Repository navigation
feat(api): cursor fingerprinting — CURSOR_STALE (policy R5) - #249
Merged
Merged
Conversation
Cursors become "<index>:<fnv32 of the full result set>" (computed over the serialized bytes pagination cuts on). A page request whose fingerprint no longer matches the current set errors with CURSOR_STALE instead of serving a shifted page; malformed cursors are rejected; bare-number cursors are accepted for legacy callers (unchecked). Prerequisite shipped in the same change: all sort tie-breaks were Math.random(), which made any multi-page drain of a default-sorted list nondeterministic. Tie-breaks are now deterministic (name), giving result sets a stable total order (cf. Determinacy). - paginateItems: cursor parse + fingerprint verify; nextCursor: string|null - schemas: cursor fields accept number|string, described as opaque - tests: 4 new R5 cases (stale after mutation, clean drain via string cursors, malformed rejected, legacy numbers) - full suite 196/196; fuzz clean Co-Authored-By: Claude Code <noreply@anthropic.com>
Contributor
Per-op benchmark — p50 cycles (base vs head)base runs: 5, head runs: 5 (median of mins) · freq-correction ×1.0062 · threshold head−base > max(40 cyc, 5%)
* head frequency-corrected to base via the Regressions detected. |
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The last piece of
docs/api-error-policy.md: pagination can no longer serve a silently shifted page.What changed
"<index>:<fnv32(result set)>"— the fingerprint is computed over the serialized bytes pagination boundaries are cut on. If the set changed between pages (content or order), the request fails with a visibleCURSOR_STALEtelling the caller to re-run and page from the start. Malformed cursors are rejected the same way; bare-number cursors are still accepted for legacy callers (unchecked, documented).Math.random(), which made any multi-page drain of a default-sorted list inherently nondeterministic — the exact silent-corruption class R5 exists to catch. Tie-breaks are now deterministic (by name), so result sets have a stable total order. This also explains several historical pagination false alarms in the KB.nextCursortype:string | null; cursor schemas acceptnumber | string.Evidence
API cursor integrity (R5)tests: stale-after-mutation →CURSOR_STALE; unchanged set drains cleanly via string cursors (no dupes, no gaps); malformed cursor rejected; legacy numeric cursor still works.KB notes at release time:
H_PaginationFalseAlarmloses its "exhaust cursors" caveat once sets are fingerprinted, and absence conclusions become safe for paginated reads.🤖 Generated with Claude Code