2026-07-17 — XP-143 keyset pagination for settlement lists
What shipped
Section titled “What shipped”packages/service/src/finance/list-student-settlements.ts+list-tutor-settlements.ts— forward-only keyset:wherecomposes the optionalstatuseq with the seek tuple(created_at, id) DESCvia RQBv2RAWon the aliased root table, fetchlimit+1to detect more, response{ items, next }(was a bare array).nextset only when a full page returned.packages/contract/src/service/finance/list-student-settlements.schema.ts+ tutor twin — input+limit(default 20, max 100)+nextcursor; response shape updated.apps/web/src/lib/api-client.ts+routes/admin/finance/settlements.tsx+settlements-tutor.tsx—SettlementsPagination(Awal reset / Berikutnya next, no Back), cursor state,data.items, reset-on-filter effect.packages/service/src/finance/list-pagination.integration.test.ts— +2 cases (student, tutor);apps/web/e2e/settlements-pagination-visual.spec.ts(new 3-viewport visual e2e).
Key decisions
Section titled “Key decisions”- Mechanical mirror of the shipped XP-134 keyset pattern (list-refundable-settlements / list-refunds → itself mirrors users.list). No new pagination shape invented; per-endpoint file duplication is the endorsed house style, not a smell.
- Selection stays per-page (forward-only + reset-to-first, no Back); bulk mark-paid invalidates the
allquery key so it covers every cursor.
Gotchas / lessons
Section titled “Gotchas / lessons”- The reset-on-filter
useEffectneedsbiome-ignore useExhaustiveDependencies—statusFilteris a trigger-only dep the body doesn’t read; this rule is NOT downgraded to warn inbiome.json, so without the ignorebun run linthard-fails (2 errors). Matches sidebar.tsx / review-apply-dialog.tsx convention. - Full
bun run typecheckhalts on a pre-existingapps/docsTS18003 “no inputs” quirk, unrelated to this diff (confirmed via git stash on clean tree).
Reference changes
Section titled “Reference changes”None. No canonical doc (plans/ARCHITECTURE.md, plans/scope/*.md, .claude/rules/*.md) changed.