본문으로 건너뛰기
cccskills
무료GitHub에 공개

verify-pagination

Prove the next page of a list, or the next batch of an infinite scroll, loads NEW rows from the server, repeats none, and that the end of the list is handled. Use when pagination or infinite scroll was added or changed, when a list shows the same rows twice or skips some, or when the last page still offers a next control.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

설치 방법 보기

포함된 파일(1)

  • SKILL.md5.7 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

The second page is where lists break

A list that renders its first page proves the first request works. The second page is where the offset is off by one, the cursor is reused, the same rows come back, or the append replaces what was there. The last page is where the "Next" button still fires a request for rows that do not exist.

Reticle can check all of that in the running app, against the requests the page really made. Not installed? RETICLE_INSTALL_SOURCE=npx_skill npx @reticlehq/server@latest init, then the install-and-verify skill.

Confirm the session can observe

reticle_session({ action: "list" })

A hidden or throttled tab can accept a click and render nothing, and an infinite scroll that loads on an intersection observer may never fire at all. If the session is not healthy, get a context that is, for example a leased tab (reticle_run({ tool: "reticle_lease", args: { action: "acquire", url } })). Otherwise report the run as blocked, not passed.

1. Record the first page

Write down what is on screen before you touch anything, or you have nothing to compare the next page against:

reticle_look({ action: "find", sessionId, by: "role", value: "row" })

Use whatever the rows really are (row, listitem, article, or a testid). Keep the visible text of every row, not a sample: a repeat from the middle of page 1 is as much a bug as one from the top. If the result says it was truncated, scope the find to the list (scope: "<the list's ref>") until it is not, or say which rows you could not see.

2. Load the next page, and name the consequence first

Name what must happen before the click, not after. For numbered or next/previous pagination, the page is replaced, so the first page's rows must be gone once the new request has answered:

reticle_act_and_wait({ sessionId, ref: "<next control>", action: "click", until: { kind: "allOf", predicates: [
  { kind: "net", urlContains: "/api/items", ok: true },
  { kind: "text", contains: "<first row of page 1>", absent: true },
  { kind: "text", contains: "<last row of page 1>", absent: true },
]}})

For "Load more" or infinite scroll, the page is appended, so the first page must still be there and the request must have answered. Use the action that really triggers the load: click for a "Load more" button, scrollIntoView on the list's end for a list that loads when scrolled.

reticle_act_and_wait({ sessionId, ref: "<load more button>", action: "click", until: { kind: "allOf", predicates: [
  { kind: "net", urlContains: "/api/items", ok: true },
  { kind: "text", contains: "<first row of page 1>" },
]}})

If the list pages on the client (all rows arrive in one response and the page only slices them), no request fires, and a net clause would wait for one that never comes. Drop it, keep the text clauses, and say in your report that the server's paging was not exercised.

urlContains must name the list's own endpoint. A predicate that any request satisfies proves nothing about this list.

3. Prove the rows are new, and none repeat

Read the rows again:

reticle_look({ action: "find", sessionId, by: "role", value: "row" })

Compare the text with what you recorded in step 1:

  • Paged: no row of page 2 may be any row of page 1. An overlap anywhere in the list means the offset or cursor did not move.
  • Appended: every page-1 row appears once, and the new ones follow them. A page-1 row that now appears twice means the batch was appended twice or re-fetched.

That comparison is evidence you read, so turn the result into a verdict. Take one row that is only on the new page and assert it:

reticle_assert({ sessionId, predicate: { kind: "text", contains: "<a row only on the new page>" } })

If the lists overlap, you have found the bug. Report which rows repeated, and do not report a pass.

4. Prove the end of the list is handled

Go to the last page (or scroll until the list stops growing) and prove the control is off:

reticle_assert({ sessionId, predicate: { kind: "anyOf", predicates: [
  { kind: "element", query: { role: "button", name: "Next" }, state: "disabled" },
  { kind: "element", query: { role: "button", name: "Next" }, absent: true },
]}})

For an infinite scroll, scroll to the end once more, then let the page go quiet before you claim it asked for nothing. An observer that fires after a delay would otherwise land after a zero reading had already passed:

reticle_act({ sessionId, ref: "<the list end>", action: "scrollIntoView" })
reticle_assert({ sessionId, timeout_ms: 5000, predicate: { kind: "allOf", predicates: [
  { kind: "settled", quietMs: 1500 },
  { kind: "net", urlContains: "/api/items", count: 0 },
]}})

A list that keeps requesting pages past the end is a real finding even when nothing new renders.

Honesty

Only verified: "yes" is a pass. For a client-side list, a yes covers the rows you asserted and nothing about the server: say so. Say which endpoint you asserted on, and report any step you could not reach as unknown, not passed.


Capability reference: curl https://docs.reticle.sh/capabilities.md. Everything else: curl https://docs.reticle.sh/llms.txt.

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

Test-driven development for behaviour a unit test cannot reach, by writing the expectation against the running app before writing the code. Declare the consequence first, watch it fail, implement, watch it pass. Use when building a user-facing feature, when the user asks for TDD on UI or full-stack work, when a unit test cannot express the outcome that matters, or when you want a red-green loop that runs against the real app instead of mocks.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

reticlehq/reticle1.3천2026년 10월 21일 업데이트

Sweep a whole running web app for what is broken, without writing a script or knowing the codebase. Clicks every reachable control and reports dead buttons, console errors, failed requests, and places where the API and the screen disagree. Use on an unfamiliar codebase, before a release, after a big merge or dependency bump, when the user asks for a smoke test or a health check, or when someone says "just check everything still works".

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

reticlehq/reticle1.3천2026년 10월 21일 업데이트

Find out why something in a running web app does not work, when the console is empty and the code looks correct. Reads the click, the request, the store and the console together and returns the file:line to open. Use when a button does nothing, a form will not submit, data will not load, a page renders blank or stale, a modal will not close, or the user says "it's broken" and the code review says it is fine.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

reticlehq/reticle1.3천2026년 10월 21일 업데이트

Check that the UI you actually rendered uses the design system, by reading computed styles in the running app against the project's design tokens. Catches hardcoded hex colors, off-palette backgrounds, invisible or unusable controls, and animations that never ran. Use after building or restyling a component, when a design review is wanted, when a UI looks slightly off but nobody can say why, or when a design system exists and nothing checks whether the code follows it.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

reticlehq/reticle1.3천2026년 10월 21일 업데이트

Drive and verify an Electron or Tauri desktop app from the inside, including the main-process and Rust IPC calls a browser tool cannot see. Use when a desktop app needs testing, when a feature works in the browser but not in the packaged app, when an IPC or invoke call needs proving, when a desktop screenshot or visual diff is wanted, or when you need a headless run of a desktop UI in CI.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

reticlehq/reticle1.3천2026년 10월 21일 업데이트

Find out why the tests pass but the app is broken. Catches false greens: a green suite over a feature that does not work, a mocked API standing in for a real one, an assertion that holds no matter what the app does, a click handler wired to nothing. Use when the suite is green and the user says it is broken, when a test never fails, when coverage looks fine but bugs still ship, or before trusting a passing run you did not watch.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

reticlehq/reticle1.3천2026년 10월 21일 업데이트

reticlehq의 스킬 모두 보기

이 스킬의 문제 신고