Usability Review

A usability review asks whether the user got the job done and where the experience got in the way. Every finding is a claim code verified: a model never decides on its own that something is a problem, and never writes the finding's text. It is advisory — a finding never sets a non-zero exit or gates a run.

One name. The product surface calls this the usability strategy (--strategy usability), matching the CLI. "UX review" is the same feature — you may see it used informally elsewhere on this site — but the canonical name to search and script against is usability.

Two ways to run it

Live, as an exploration strategy against a running app:

jevitate explore --url http://localhost:3000 \
  --strategy usability --goal "check out with a saved card" \
  --app-class consumer --real

# optional: independent checks decide whether the job was done, as on a goal run
jevitate explore --url http://localhost:3000 --strategy usability \
  --goal "check out with a saved card" --app-class consumer \
  --success urlIncludes:/order/confirmed --real

# opt in to the fail-safe destructive-control probe (prefer staging), with product facts
jevitate explore --url https://staging.example.com/settings --strategy usability \
  --goal "manage team members" --app-class admin \
  --product .jevitate/product.json --probe-guards --real

Offline, over a Recording you already captured (jevitate record or a goal run's output):

jevitate ux ./checkout.recording.json --app-class consumer --real

# optional persona + the job the flow pursues, for sharper relevance
jevitate ux ./checkout.recording.json --app-class admin \
  --persona "first-time buyer" --job "check out with a saved card" --real

The offline review reads the live run's evidence sidecar (<stem>.evidence.json) when it is there, so it reproduces the live run's findings.

How a finding is made

  1. A claim starts from what the run observed: a guard probe, your product facts, or friction the run hit (a retry, a long wait, a dead end, an error, a backtrack, an abandoned form, an unfinished job).
  2. Jev categorizes friction: a claim type from a closed list (or not-a-problem) and the control it is about, picked from the page's real controls.
  3. Code verifies every claim. One that fails is dropped and counted in report.claims, never shown.
  4. Duplicates merge by claim type, page and control.
  5. Two yes/no questions grade it: "Do we need this?" and "Do we need this to ship this feature?". Code maps the answers to actionable, relevant-minor, generic or wrong.
  6. The text comes from a template over the verified fields (control, page, blocked request, quoted text, expected fact). --polish (opt-in) rewrites the recommendation with one generation call.

Claim types: destructive-unguarded, fact-conflict, next-step-unclear, no-feedback, blocked-action and error-unrecoverable. Each is cited under the heuristic it violates (Report Schema). On a live run each verified finding gets a cropped, secret-masked screenshot with the cited control boxed.

Guard probes: --probe-guards

Clicking destructive controls is off by default. Without the flag nothing is clicked, and a "this deletes without asking" claim is reported unverifiable (in coverage.skipped), never a finding. With it, after the run each destructive control ("Delete", "Remove", "Revoke"…) is clicked once on a fresh page to see whether a confirm dialog, page dialog or confirmation page comes first. The probe is fail-safe:

  • It aborts every write and every destructive-looking request, whatever the method (URL, query, RPC name or body).
  • It refuses pages with an open WebSocket or EventSource, or a service worker.
  • It never answers a dialog, and never clicks a control matching --deny.

A server that destroys data on a harmless-looking GET can't be recognized: probe a staging environment.

Product facts: .jevitate/product.json

State what the product is, and the review checks the screens against it in code: a wrong price or trial is a fact-conflict, a missing or disabled intended next step is next-step-unclear. Pass --product <file> or keep it at .jevitate/product.json.

{
  "version": 1,
  "currency": "USD",
  "plans": [
    { "name": "Starter", "prices": [{ "amount": 29, "interval": "month" }], "trialDays": 14 },
    { "name": "Pro", "prices": [{ "amount": 149, "interval": "month" }], "trialDays": 14 }
  ],
  "journeys": [{ "name": "Set up a workspace", "routes": ["/onboarding", "/projects/:id"] }],
  "pages": [{ "route": "/onboarding", "nextStep": "Create project" }]
}

The file is validated strictly; every problem is listed in one refusal (E_UX_PRODUCT_INPUT, exit 64) before a browser opens. Without it, the report says that prices and next steps went unchecked.

What --app-class actually controls

--app-class is required for both paths. It is a calibration signal: it tells Jev what kind of app this is (consumer, admin, internal, etc.) so it judges friction sensibly for that context.

It is not a persona chooser. --app-class does not select or imply a user persona; it only calibrates the review for the kind of app. The live review is calibrated against a default persona, "first-time user". The offline path (jevitate ux <recording>) accepts an explicit usability --persona. Don't confuse it with explore --persona name=storageState, which runs the same mission once per signed-in session and diffs them (see Flags Reference).

Viewport and device

By default every run, the usability review included, uses Playwright's default viewport: 1280×720, desktop, no touch. --viewport <W>x<H> (e.g. --viewport 375x812) or --device "<name>" (e.g. --device "iPhone 13") change it; they are mutually exclusive. Below 1024px the horizontal-overflow and vertical-clipping checks run too (text cut off by a fixed-height box or above the page top): pure DOM geometry, reported as a signal finding in the usability review. --check-overflow turns them on at any width; --ignore-overflow <selector> excludes intentional cases.

Still calibrating. Findings are advisory. Jev's friction categorization and the two-question grade cutoffs still need real-model calibration, so every grade is shown by default (filter with --show), and the report says so in calibrationCaveats. Expect far fewer findings than in 0.2.0: a finding exists only when code verified its claim.

Going deeper

  • Report Schema — claims, grade, coverage, suppressed, and what clean does and doesn't guarantee.
  • Flags Reference — every flag per strategy.
  • MCP & CLI — full command syntax for both the live and offline paths.
  • Testing Modes — how usability fits among the other five jevitate explore modes.

Coming soon: Journeeze adds what people think of the same journeys: likes, dislikes and what they don't understand, clustered into GitHub issues. Get early access.