Skip to content

feat(review-desk): Page the Review item list with a cursor - #2480

Open
sentry-junior[bot] wants to merge 2 commits into
mainfrom
ref/review-desk-paging
Open

sentry-junior[bot] wants to merge 2 commits into
mainfrom
ref/review-desk-paging

Conversation

@sentry-junior

@sentry-junior sentry-junior Bot commented Oct 31, 2026

Copy link
Copy Markdown
Contributor

The review desk list returned at most 100 items and had no way to read more. So the archive cut off old items, and nothing showed that it did.

GET /api/review-items now pages:

  • It takes cursor and limit (default 50, max 100). It returns nextCursor when more items follow.
  • The order is updated_at desc, id desc, as before. searchReviewItems takes an after position in that order.
  • The cursor uses the same shape as memory paging: base64url JSON with the position, the list filters, and a version. A cursor from a different list (view, archived, Conversation, or search) gets a 400.
  • The review desk uses useInfiniteQuery and the shared LoadMorePagination button, like the memory page. The Conversation review bar uses the same hook.

store.ts changes only touch search and order, so this PR can merge in either order with the work that moves the visibility rules into access.ts.

An item that changes while a person reads page 1 moves to the top, so page 2 does not show it. A refresh shows it again. Because updated_at only goes up, an item cannot show on two pages.

Mock reporting still returns { items: [] }, which is valid with the optional nextCursor, so mock-reporting/routes.ts does not change.

Checks:

  • pnpm typecheck and pnpm lint pass.
  • vitest: tests/integration/api/review-items/actions.test.ts (extended with the paging contract) and tests/integration/review-item-tools.test.ts pass.
  • The dashboard unit tests pass (326).
  • Not run locally: the dashboard e2e test review-desk.spec.ts. Playwright could not find Chrome in the sandbox, so CI runs it.

Part of the review desk cleanup for #2401.

via David Cramer.

--

View Session [Sentry]

The review desk list returned at most 100 items with no way to read more,
so the archive cut off old items without a sign. The list API now takes
`cursor` and `limit` and returns `nextCursor`, in the order
updated_at desc, id desc. The cursor names the list filters, so it cannot
page a different list. The review desk shows Load more.

Co-Authored-By: David Cramer <david@sentry.io>
@vercel

vercel Bot commented Oct 31, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
junior-docs Ready Ready Preview Oct 31, 2026 4:50am UTC

Request Review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 9547393. Configure here.

const items = useMemo(
() => (query.data?.pages ?? []).flatMap((page) => page.items),
[query.data?.pages],
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Paged list can show duplicate items

Medium Severity

useReviewItemsData concatenates every loaded page with no id dedupe. After a review action, invalidateQueries refetches each page with its original cursor, so a removed item shifts page 1 down and the same item can appear again from page 2. The shared memory pager already collapses records by id for this reason.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 9547393. Configure here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Declining this one. On invalidate, TanStack Query v5 refetches an infinite query one page at a time. It takes the cursor of each page after the first from the page it just fetched, with getNextPageParam (see infiniteQueryBehavior.js). It does not reuse the old cursors. So when an item leaves page 1, page 2 starts after the new last item of page 1. Also, updated_at only goes up, so a changed item can only move above an old cursor, never below it. A dedupe by id would have nothing to remove.

The bar counted only the first page of open items. It now shows 50+ when
more items follow, and its side panel can load them.
@github-actions

Copy link
Copy Markdown
Contributor

1 screenshot change — 1 changed · 0 added · 0 removed

Review screenshots in Frameshift

Location Detail · Desktop
Location Detail · Desktop
Changed

This branch was successfully deployed

1 active deployment
Preview – junior-docs — cc083a1f Deployed Oct 31, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk: high PR risk score: high

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant