Protocol browser anti stall
Skill kensaurus/cursor-kenji/skills/protocol-browser-anti-stall
Prevent browser automation from freezing, stalling, or colliding between parallel agents, and enforce manual, headed, real-user driving (never scripted). Standardizes on the playwright-cli (`npx --yes @playwright/cli@latest`) with named sessions (`-s=<name>`) so multiple agents each get their own isolated browser — replacing the single-instance Playwright MCP, where one shared profile could only be locked by one process at a time. Covers session naming, headed mode, persistent auth profiles, the wait/anti-loop budget, evidence-before-retry, artifact paths, and cleanup. Use BEFORE any browser automation — testing webapps, user-story walkthroughs, QA/UX audits, visual verification, or any task that drives a browser.From its SKILL.md
npx -y skills add kensaurus/cursor-kenji --skill protocol-browser-anti-stallAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 8 stars8 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its file declares
Copied from the file, not written here
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
8.9 KB, ~2.1k tokens by cl100k_base, as published. Nobody here has run it
Browser Anti-Stall Protocol (playwright-cli)
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.
Invocation — always this form
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 -ginstall. Underfnm/nvmthe global prefix is per-shell and disappears;npxalways resolves. --json/--raware available when you need machine-readable output.
0. Manual & headed — never scripted (read first)
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.
- Headed, always. The CLI defaults to headless — you MUST pass
--headedonopen. If you cannot see the window, say so rather than proceeding blind. - One real action at a time.
click,type,fill,select,hover,press,dragexactly as a user would. Never chain a whole flow into one code snippet. eval/run-codeare 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.- No test files, no runner. Do not write
*.spec.ts, runnpx playwright test, or use codegen. You are here to experience the flow, not automate past it. - 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
$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
openstarts a browser;gotonavigates an already-open one. Callingopentwice on the same session is wasteful — usegoto.- Close only your own session. Never
close-allwhile another agent may be mid-test. - Add
--browser chrome|firefox|webkit|msedge,--device "iphone 15", or--mobileonopenwhen the task calls for it.
2. Navigation guard
After every open / goto / reload:
snapshot— confirm the URL changed and the page has content.- If blank or unchanged →
sleep 2→snapshotagain. - 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
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
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, orfind). - If a spinner is showing, wait for it to reach
state: 'hidden'rather than sleeping.
5. Anti-loop: max 4 attempts per goal
| 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
When something is not working, gather evidence FIRST, then form a hypothesis:
console— JS errors, warnings (console errorto filter by level)requests— pending/failed calls;request <n>/response-body <n>for detailsnapshot— the actual DOM state, not what you assumescreenshot --filename .playwright-mcp/<name>.png— visual state
Only retry once you have a new hypothesis grounded in that evidence.
7. Timeout budget
| 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
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
- 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
.ymlfiles to.playwright-cli/in the working directory — also gitignored, never committed. - Sweep any stray root-level
*.png/*.loginto.playwright-mcp/before ending the session.
10. Parallel agents
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-allsessions you did not open. listshows 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.
What ships with it: 2 files
11.5 KB alongside SKILL.md