One line. Consequential clicks and submits are held, checked, and dropped if they break a rule.
npx getclaros in your project sets this up: it installs getclaros-browser with your package manager and, for Playwright Test, writes claros.fixture.ts (every test’s page guarded). Then change from "@playwright/test" to from "./claros.fixture" (the path init prints) in your specs. By hand: npm install getclaros-browser and the code below.
import { guard, ClarosStop } from "getclaros-browser";const g = await guard(page); // CLAROS_URL, CLAROS_KEY from the envtry { await page.getByRole("button", { name: "Issue refund" }).click();} catch (e) { if (e instanceof ClarosStop) console.log(e.message); // the rule, the expert's words, who to ask else throw e;}
Works on any web app: nothing in it knows an app’s names, selectors or URLs.
A script in every frame holds commits before the app’s handlers run: a POST form submit, Enter in a form, or a click on a button, link or menu item whose name commits (submit, save, send, pay, approve, delete, deploy, merge, refund … in 7 languages). Reads, navigation and typing pass.
It reads the record around the button from the DOM: labels and values, tables and ARIA grids as rows, review lists, open shadow roots.
It asks POST /v1/check/page. No fields at all (canvas, cross-origin iframe)? A screenshot goes to the screen fallback.
Allowed: the action is replayed once and reaches the app. Stopped (stop or ask): it’s dropped and your call throws ClarosStop. Nudged: replayed like an allow; the nudge (rule, the owner’s words, suggested fix) goes to onNudge, guard.nudges and console.warn.
A form.submit() from a script (no submit event) is checked at the network from its form data and aborted on a stop.
on an ask, wait this long for the rule owner’s answer; the click is replayed only on an approval of this action. The Playwright call blocks meanwhile, so raise test.setTimeout where an ask can happen. 0: stop at once
actor, workflowId
logged / pin a map
onDecision
called for every check
onNudge
called with {action, url, text, violations, checkId} when a nudged action went through
ClarosStop has rule, quote, expert, whoToAsk, checkId, result, and message (the same text as the MCP guard). guard() returns {decisions, stops, nudges, drain()}: drain() waits for pending checks and returns the first stop.Stops caused by calls Playwright doesn’t wrap (for example page.evaluate) are still held and checked; they land in stops and onDecision.