Skip to content

fix(ui): AuditFeed "Load more" can drop or reorder already-visible rows because it has no offset/cursor #7438

Description

@JSONbored

Context

AuditFeed (apps/loopover-ui/src/components/site/audit-feed.tsx) fetches
/v1/app/skipped-pr-audit with only a limit query parameter (built by
buildSkippedPrAuditPath in audit-feed-model.ts, lines 39–51 — the function accepts limit,
repoFullName, reason, since; there is no offset/cursor/page parameter anywhere in this
model). loadMore (line 111–113):

const loadMore = () => {
  setLimit((current) => Math.min(current + DEFAULT_LIMIT, MAX_LIMIT));
};

increases limit from 50 toward a 100-item cap and re-runs the exact same query from the start via
load() (triggered by the queryPath/load dependency chain, lines 49–94) — there is no offset, so
every "Load more" click re-requests "the N most recent skip events," not "the next N after what's
already shown." For a genuinely live feed like this (new skip decisions land continuously — the
AuditFilters UI and hasMore/"More available" pill both imply an ongoing stream, not a static
export), if more than DEFAULT_LIMIT (50) new events land between the initial load and a "Load more"
click, some of the rows a user is currently looking at can silently fall out of the newly-fetched
top-limit window and disappear from the table — a real, user-visible pagination inconsistency, not
a cosmetic one.

The correct pattern already exists two files over in the same directory:
dead-letter-queue-panel-model.ts models a page as { limit, offset, total } and
dead-letter-queue-panel.tsx computes hasNext/hasPrevious from offset/limit/total and
advances/retreats by adjusting offset, not by growing a single ever-larger limit. This is a clear,
existing, in-repo precedent to follow — the "how" is not an open design question.

Requirements

  • The deliverable is a real offset (or cursor) parameter, not a bigger limit. Add
    offset: number to SkippedPrAuditExport's request shape (mirroring
    DeadLetterQueuePage's offset/total fields) and to buildSkippedPrAuditPath's options, and
    have the backend route (/v1/app/skipped-pr-audit — locate its handler under src/ and thread the
    parameter through the same way the dead-letter-queue route already does for its own offset) return
    enough metadata (a total count, or a hasMore computed from offset + limit < total) to know when
    to stop paging.
  • loadMore must advance the offset by the current page's item count (or by DEFAULT_LIMIT) and
    fetch only the next page, then append the new rows to the existing data.items array in
    component state rather than replacing it wholesale — so rows already rendered are never re-derived
    from a re-run of the base query.
  • Anti-pattern, explicit: increasing the limit query parameter and re-issuing the same
    from-the-start query — the pattern this issue exists to remove — does not satisfy this issue,
    even though it currently "looks like" pagination (it produces a larger result set that visually
    resembles "more rows"). A PR that only changes MAX_LIMIT or the increment size without introducing
    a real offset/cursor and an append-not-replace update does not resolve this issue.
  • Preserve existing filter behavior: applyFilters/resetFilters must still reset to the first page
    (offset 0) exactly as they reset limit to DEFAULT_LIMIT today.

Deliverables

  • SkippedPrAuditExport (or its request/response types) carries a real offset/cursor and enough
    metadata to compute whether more pages exist.
  • buildSkippedPrAuditPath accepts and encodes the offset/cursor parameter.
  • The backing API route reads and honors it (paginating server-side, not just capping a bigger
    response).
  • AuditFeed's loadMore appends the next page's rows to existing state instead of replacing the
    whole list from a bigger limit.
  • applyFilters/resetFilters reset paging state to the first page.

Test Coverage Requirements

This touches both apps/loopover-ui/** (the AuditFeed component/model — outside Codecov's
coverage.include/apps/** ignore, so no patch-coverage obligation there, but the component's own
apps/loopover-ui/src/components/site/audit-feed.tsx/audit-feed-model.ts test files must still
cover the new append-not-replace behavior and the offset-advance math with real assertions) and
the backend route under src/ (the actual API handler for /v1/app/skipped-pr-audit) — any line/branch
changed there is subject to the 99% Codecov patch gate, hard, including both sides of any new
offset-related conditional (e.g. "more pages exist" vs. "this was the last page").

Expected Outcome

Clicking "Load more" on the skipped-PR audit feed always appends genuinely new rows after the ones
already shown, and never causes a previously-visible row to shift position or disappear, regardless
of how many new skip events have landed on the server since the initial load.

Links & Resources

  • apps/loopover-ui/src/components/site/audit-feed.tsx:44,49-58,111-113,251-260
  • apps/loopover-ui/src/components/site/audit-feed-model.ts:39-51
  • apps/loopover-ui/src/components/site/dead-letter-queue-panel-model.ts:10-29 (the pattern to mirror)
  • apps/loopover-ui/src/components/site/dead-letter-queue-panel.tsx:177,221,242,368,379

Metadata

Metadata

Assignees

No one assigned

    Labels

    gittensor:bugGittensor-scored bug fix — scores a 0.05x multiplier.help wantedExtra attention is needed

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions