kensaurus/cursor-kenji

protocol-browser-anti-stall

Browser-session guardrail for Playwright CLI: use headed, named, isolated sessions; prevent parallel collisions and recover stalls without scripted shortcuts. Read before browser work or when automation freezes. Product QA behavior remains with the calling test/audit skill.

First seen Jun 15, 2026

Installation

$ npx skills add kensaurus/cursor-kenji --skill protocol-browser-anti-stall

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from kensaurus/cursor-kenji · top by installs.

npx skills add kensaurus/cursor-kenji

Browse all from kensaurus/cursor-kenji

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Stars 9
License LICENSE
Default branch main
Open issues 0
Status Active

Skill metadata

Parsed from SKILL.md frontmatter.

LicenseMIT

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 10,276 B
  • docs SUMMARY.md 309 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 27 installs

SKILL.md

Browser Anti-Stall Protocol (playwright-cli)

Degree of freedom: LOW. Almost every step is exact. Only "why it stalled" and the next hypothesis are [HIGH freedom].

Apply these rules to EVERY browser action. No exceptions.

This repo drives browsers with playwright-cli, not the Playwright MCP. The MCP exposes one browser per server and a persistent profile can only be locked by one process at a time, so parallel agents on the same repo fight over tabs and profile locks. The CLI gives every agent its own isolated browser via -s=<session>, costs far fewer tokens (no tool schemas or verbose trees loaded into context), and runs natively in parallel shells.

Read references/mcp-to-cli-map.md if you encounter old browser_* MCP tool calls — it maps every tool to its CLI command. Read references/playwright-session-coordination.md before your first command — session naming, persistent logins (incl. the Google/CDP block), and cleanup.

How to reason

  1. Observe — snapshot, screenshot, console, requests, and the session name
  2. Interpret — stale ref, SPA hydration, pending request, or a real blocker
  3. Classify — one new-hypothesis retry / 2s sleep-cycle / BLOCKER report
  4. Recover — one real user action, then look; never eval to click

Worked example

Observe: checkout click; URL still /cart; console hydration warning; POST /api/checkout pending; session -s=qa-checkout.
Interpret: SPA not ready + possibly a stale ref — not "the button is missing".
Classify: attempt 2 — find/waitFor a landmark, fresh snapshot, then click. Not run-code to submit.
Stop at 4: still stuck → BLOCKER with console + requests + screenshot.

Self-critique before reporting

  • Headed + -s= — every call; never a shared session name
  • Inspection-only evaleval / run-code never click, type, or submit
  • Evidence before retry — max 4 attempts, then BLOCKER
  • Right owner — product QA behavior stays with the calling test/audit skill

Invocation — always this form [LOW freedom — run exactly]

PW="npx --yes @playwright/cli@latest"     # portable; survives fnm/nvm version switches
$PW -s=<session> <command> [args]
  • -s=<session> is mandatory on every call. Name it after your task or branch

(-s=qa-checkout, -s=audit-ux-home). Two agents must never share a session name.

  • Do not rely on a global npm i -g install. Under fnm/nvm the global prefix is

per-shell and disappears; npx always resolves.

  • --json / --raw are available when you need machine-readable output.

0. Manual & headed — never scripted (read first) [LOW freedom — run exactly]

You are driving a real, visible browser to feel what a user feels. A green script proves nothing about UX — see the screen and watch the logs.

  1. Headed, always. The CLI defaults to headless — you MUST pass --headed on open.

If you cannot see the window, say so rather than proceeding blind.

  1. One real action at a time. click, type, fill, select, hover, press, drag

exactly as a user would. Never chain a whole flow into one code snippet.

  1. eval / run-code are inspection-only. Use them ONLY to read state (DOM, computed

styles, storage, perf) or to wait for an element — never to click, type, navigate, or submit. Driving the UI through code bypasses real events and hides the bug you are hunting.

  1. No test files, no runner. Do not write *.spec.ts, run npx playwright test, or use

codegen. You are here to experience the flow, not automate past it.

  1. Look after every action. Fresh snapshot + screenshot + console + requests, plus the

dev-server terminal. Real pain surfaces on screen and in logs, not in an assertion.

1. Session lifecycle [LOW freedom — run exactly]

$PW -s=qa-checkout open --headed http://localhost:3000    # start (once)
$PW -s=qa-checkout goto http://localhost:3000/cart        # navigate within the session
$PW -s=qa-checkout snapshot                               # get refs
$PW -s=qa-checkout close                                  # end YOUR session when done
$PW list                                                  # see all sessions (status, profile, headed)
$PW close-all                                             # only when you own every session
$PW kill-all                                              # last resort: stale/zombie processes
  • open starts a browser; goto navigates an already-open one. Calling open twice on the same

session is wasteful — use goto.

  • Close only your own session. Never close-all while another agent may be mid-test.
  • Add --browser chrome|firefox|webkit|msedge, --device "iphone 15", or --mobile on open

when the task calls for it.

2. Navigation guard [LOW freedom — run exactly]

After every open / goto / reload:

  1. snapshot — confirm the URL changed and the page has content.
  2. If blank or unchanged → sleep 2snapshot again.
  3. Max 3 cycles (~6s). Still not loaded → report a blocker (§8) and move on.

Never assume navigation succeeded without a snapshot to confirm it.

3. Waiting — there is no wait command [LOW freedom — run exactly]

Playwright auto-waits for actionability on click/fill/select, so most explicit waits are unnecessary. When you genuinely must wait:

Need Do this
Fixed short pause sleep 2 in the shell — never more than 3s per pause
Wait for text/element run-code "async (page) => { await page.getByText('Dashboard').first().waitFor({ timeout: 5000 }); return 'ready'; }"
Wait for something to disappear ...waitFor({ state: 'hidden', timeout: 5000 })
Poll for content find "<text>" → if no match, sleep 2 → retry (max 3)

Always set an explicit timeout (milliseconds) in waitFor — the default 30s is far too long. Use the incremental pattern instead of one long block:

sleep 2 → snapshot → check ↓ not ready
sleep 2 → snapshot → check ↓ not ready
sleep 2 → snapshot → check ↓ still not ready
STOP → report blocker with evidence

This handles cold starts, SPA hydration, and slow APIs without ever blocking blindly.

4. SPA-specific rules [LOW freedom — run exactly]

SPAs (React, Next.js, Vue) fire load before hydration completes — never trust load events.

  • Wait for a specific UI landmark that proves the app rendered (run-code + waitFor, or find).
  • If a spinner is showing, wait for it to reach state: 'hidden' rather than sleeping.

5. Anti-loop: max 4 attempts per goal [LOW freedom — run exactly]

Attempt Action
1 Try the action normally
2 Alternative approach — re-snapshot for a fresh ref, try a CSS selector instead, scroll into view, or find the element
3 Gather evidence: console + requests
4 STOP. Report what blocked progress, with evidence.

Never repeat the exact same failing action without new evidence.

Fresh refs after every state change. Refs from a stale snapshot are invalid after any navigate/click/fill/hover/key press. Re-snapshot before the next interaction. click also accepts a unique CSS selector, which survives state changes better than a ref.

6. Evidence before retry [LOW freedom — run exactly]

When something is not working, gather evidence FIRST, then form a hypothesis:

  1. console — JS errors, warnings (console error to filter by level)
  2. requests — pending/failed calls; request <n> / response-body <n> for detail
  3. snapshot — the actual DOM state, not what you assume
  4. screenshot --filename .playwright-mcp/<name>.png — visual state

Only retry once you have a new hypothesis grounded in that evidence.

7. Timeout budget [LOW freedom — run exactly]

Scope Max time
Single interaction (click, fill, select) 15 seconds
Navigation + verification 30 seconds
Multi-page flow 5 minutes
Full session 15 minutes

Exceeded? Skip it and log [TIMEOUT] skipped: <step>. One stuck step must not kill the session.

8. Blocker reporting format [LOW freedom — this shape]

BLOCKER:
- Session: [-s= name]
- Page: [current URL]
- Goal: [what I was trying to do]
- Blocked by: [what prevented it]
- Evidence: [console errors / failed requests / screenshot observation]
- Suggestion: [most likely next step or manual action needed]

Actionable information beats a silent freeze.

9. Artifacts [LOW freedom — run exactly]

  • Screenshots, snapshots, and logs go under .playwright-mcp/ (gitignored):

screenshot --filename .playwright-mcp/home-390.png. Name by route + viewport/step.

  • The CLI also auto-writes snapshot .yml files to .playwright-cli/ in the working directory —

also gitignored, never committed.

  • Sweep any stray root-level .png / .log into .playwright-mcp/ before ending the session.

10. Parallel agents [LOW freedom — run exactly]

Session isolation replaces the old tab-sharing etiquette — each agent gets its own browser:

# agent A                                  # agent B (simultaneously, no conflict)
$PW -s=audit-ux open --headed …            $PW -s=qa-checkout open --headed …
  • Never reuse another agent's session name; never close/kill-all sessions you did not open.
  • list shows every session with its status, profile, and headed flag — check it before assuming.
  • Within one session, multiple tabs are still available (tab-list, tab-new, tab-select,

tab-close); the fresh-refs rule applies after every tab switch.

  • Signed-in state is shared through persistent profiles, not shared tabs — see

references/playwright-session-coordination.md.