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

verify-login-logout

Prove sign-in lands the user where they should be, sign-out really ends the session so a protected page sends them back to sign in, and an expired session asks to sign in again instead of breaking. Use when auth, a login form, a logout button, route guards or token refresh were added or changed, or when a user reports being logged out, or not logged out, unexpectedly.

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

설치 방법 보기

포함된 파일(1)

  • SKILL.md5.6 KB

SKILL.md(원문)

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

Signed in on screen is not signed in

A login form that shows the dashboard after submit proves the page changed. It does not prove the server accepted the credentials, that signing out ends anything, or that a user whose session ran out gets a sign-in page instead of a blank screen and a loop of 401s.

Reticle can check all three in the running app. Not installed? RETICLE_INSTALL_SOURCE=npx_skill npx @reticlehq/server@latest init, then the install-and-verify skill.

Before you start

  • Use the project's test account. Take credentials from its seed data, fixtures or .env.example, or ask the user. Never type a real person's password.
  • Confirm the session can observe: reticle_session({ action: "list" }). A hidden or throttled tab can accept a click and render nothing. If it is not healthy, report the run as blocked, not passed.
  • Step 3 needs a browser Reticle owns. Forcing an expired session uses reticle_network_mock, which works in a leased tab (reticle_run({ tool: "reticle_lease", args: { action: "acquire", url } })) or a driven one, and not through the always-on SDK. Without one, do steps 1 and 2 and report step 3 as unknown.

1. Sign in, and name where it must land

Get the form's refs with reticle_look({ action: "page", sessionId, mode: "interactive" }), fill the fields with reticle_act, then submit and state the consequence before you click:

reticle_act_and_wait({ sessionId, ref: "<sign-in button>", action: "click", until: { kind: "allOf", predicates: [
  { kind: "net", urlContains: "/api/login", ok: true },
  { kind: "route", pathname: "/dashboard" },
  { kind: "element", query: { role: "button", name: "Account" } },
]}})

All three, because each alone can lie: a route change can happen on the client before the server answers, and a user menu can render from a stale cache. Use the app's real origin, endpoints, landing route and signed-in marker throughout: the ones here are examples.

reticle_run({ tool: "reticle_storage", sessionId, args: {} }) shows what the app stored. Sensitive keys come back redacted and httpOnly cookies are invisible to the page by design, so this is evidence about where the session lives, not a verdict.

2. Sign out, then try to go back

reticle_act_and_wait({ sessionId, ref: "<sign-out control>", action: "click", until: { kind: "allOf", predicates: [
  { kind: "route", contains: "/login" },
  { kind: "element", query: { role: "button", name: "Account" }, absent: true },
]}})

Landing on the sign-in page proves the UI moved. It does not prove the session ended. Navigate straight to a protected page, on the app's real origin and route, and prove it refuses you:

reticle_navigate({ sessionId, url: "<the app's origin>/dashboard" })
reticle_assert({ sessionId, predicate: { kind: "allOf", predicates: [
  { kind: "route", contains: "/login" },
  { kind: "text", contains: "Sign in" },
  { kind: "net", urlContains: "/api/me", status: 401 },
]}})

The net clause is what proves the server ended the session. A client-side guard can redirect to /login off a cleared flag while the server still honours the old session, and the route and text would pass. The request the page makes to learn who is signed in must now be refused. If the page makes no such request, say so and report the server half as unknown.

A protected page that still renders after sign-out is the most important finding this skill can produce. Report it even if everything else passed.

3. Expire the session, and prove the app asks again

Sign in again (step 1). Then make the server treat the session as expired by failing the call the app uses to check it:

reticle_run({ tool: "reticle_network_mock", sessionId, args: {
  mocks: [{ urlContains: "/api/me", status: 401 }],
}})

Reload a protected page and name what must happen:

reticle_navigate({ sessionId, url: "<the app's origin>/dashboard" })
reticle_assert({ sessionId, timeout_ms: 5000, predicate: { kind: "allOf", predicates: [
  { kind: "net", urlContains: "/api/me", status: 401 },
  { kind: "route", contains: "/login" },
  { kind: "console", level: "error", absent: true },
]}})

The net clause proves the mocked 401 was the thing the app reacted to. If the app never called the mocked endpoint, a redirect that happens anyway is a coincidence, not expiry handling, so the verdict is unknown until you mock the call it really makes. The console clause matters: an app that "handles" expiry by throwing an uncaught error and showing a blank page has not handled it. Clear the mock with { clear: true } when you are done.

Honesty

Only verified: "yes" is a pass. Step 3 is a stand-in: Reticle cannot delete an httpOnly cookie or edit storage, so a mocked 401 is how the expiry is forced. Say which endpoint you mocked. Clear every mock and sign out before handing back, and never report a password, token or cookie value you saw.


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의 스킬 모두 보기

이 스킬의 문제 신고