Skip to content
Development
Skill

/ux-grill

Use when a running web app's UX has to be audited reproducibly rather than by feel — a deterministic mechanical pass over routes and viewports (axe, target size, horizontal overflow, page title, scripted journeys) followed by a screenshot-grounded interrogation of the operator,

From plugin
session-orchestrator
5044 skills14 agents26 commands10 hooks
+1
Install
$ npx -y skills add Kanevry/session-orchestrator --skill ux-grill --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/ux-grill

Context preview

The summary Claude sees to decide when to auto-load this skill.

Use when a running web app's UX has to be audited reproducibly rather than by feel — a deterministic mechanical pass over routes and viewports (axe, target size, horizontal overflow, page title, scripted journeys) followed by a screenshot-grounded interrogation of the operator,

SKILL.md

ux-grill.SKILL.md
name: ux-grill
description: Use when a running web app's UX has to be audited reproducibly rather than by feel — a deterministic mechanical pass over routes and viewports (axe, target size, horizontal overflow, page title, scripted journeys) followed by a screenshot-grounded interrogation of the operator, journey by journey, with two persona lenses from the target repo's manifest. Triggered by "grill the UX", "roast the dashboard", "UX-Audit", "/ux-grill". Bootstraps its own manifest from a loopback URL on the first run, so it never requires a hand-written file to start.
model: inherit
color: magenta
tools: Read, Grep, Glob, Bash, Write

UX-Grill Skill

> Two stages, hard boundary. **Stufe 1** measures — a Node pass over routes × viewports that calls `agent-browser` and writes JSON, with no model in the loop. **Stufe 2** grills — the `/grill` loop applied to journeys, where every claim carries a screenshot path. Severity is never a judgment here; it comes out of `schema.mjs`.

Soul Reference

Read `soul.md` in this skill directory before anything else. It defines WHO you are — the UX Interrogator — the persona-lens discipline, and the six tactics in their UX form. The rubric you measure against is `rubric-v2.md`; read it before Phase 1 so you can read `findings.jsonl` without guessing what a `checkId` means.

When to use

  • A web app is running on loopback and the operator wants its UX improved systematically, not by single impressions
  • The same audit has to be re-runnable later, so that "did this get better?" is answerable by fingerprint diff rather than memory
  • Mobile and desktop need judging against the SAME journeys, with the viewport verified rather than assumed
  • Journeys exist (or can be written) as executable `agent-browser` command lines

When NOT to use

  • A CI-shaped end-to-end run over an existing test profile → `/test` (driver + `agents/ux-evaluator.md`). `ux-evaluator` is deliberately NOT reused here: Stufe 1 is LLM-free by construction.
  • A plan, PRD or design needs stress-testing before it is built → `/grill`. There is no running app to measure yet.
  • Per-wave design drift against the design source → `design-reviewer` (SO#1300 B4). That checks whether the built thing matches the design; `/ux-grill` checks whether a person gets through the journey.
  • A native macOS app → out of scope in v2; web only.
  • A non-loopback target → refused, not degraded. Both `loadManifest()` and `collect()` assert it.

Phase 0: Target + Stufe 1

0.1 Resolve the argument

Per `commands/ux-grill.md` § Argument Validation: a loopback URL selects the bootstrap path, a file path or empty selects `DEFAULT_MANIFEST_PATH` from `scripts/lib/ux-grill/manifest.mjs`.

0.2 Bootstrap (URL, no manifest yet)

This path exists because the predecessor skill died of its absence: a tool that presupposes a hand-written manifest is never started.

1. **Ask ONCE, up to three questions in a single `AskUserQuestion` call** — the env FILE name and the two login env NAMES. Never a value; the manifest and everything downstream carry names only.

AskUserQuestion({
  questions: [
    { question: "Which env file of the target repo holds the login credentials?",
      header: "Env-Datei",
      options: [
        { label: ".env.local (Recommended)", description: "The usual dev file. Cost: none — only the NAME is stored; values stay in the gitignored file." },
        { label: ".env.e2e.local", description: "A separate e2e file, where one exists. Same storage contract." },
        { label: "Ohne Login", description: "No credentials at all — personas stay empty and only the unauthenticated navigation is crawled. Cost: journeys behind the login are invisible." },
        { label: "Other / describe below", description: "Name a different file." } ],
      multiSelect: false },
    { question: "Which env variable holds the login e-mail?",
      header: "Mail-Env",
      options: [
        { label: "TEST_USER_EMAIL (Recommended)", description: "The most common name in the env file you picked. Cost: none — the NAME is stored, the value never is." },
        { label: "LOGIN_EMAIL", description: "Use when the target repo names it that way." },
        { label: "Other / describe below", description: "Name the variable yourself; read it from the env file first." } ],
      multiSelect: false },
    { question: "Which env variable holds the password?",
      header: "Pass-Env",
      options: [
        { label: "TEST_USER_PASSWORD (Recommended)", description: "Pairs with the e-mail name above. Same storage contract: only the NAME reaches the manifest." },
        { label: "LOGIN_PASSWORD", description: "Use when the target repo names it that way." },
        { label: "Other / describe below", description: "Name the variable yourself." } ],
      multiSelect: false }
  ]
})

2. **Crawl.** Open an `agent-browser` session of your own (`session id --scope worktree --prefix uxgrill` — never the shared default daemon), log in with the named credentials when they were given, `snapshot`, and collect the links inside the `navigation` landmark plus the visible links on the landing screen. Each discovered route becomes `{path, title}`. 3. **Write.** `buildBootstrapManifest({baseUrl, build, envFile, loginEnvEmail, loginEnvPassword, routes})` → `writeBootstrapManifest({repoRoot, text})`. `journeys[]` is always empty; personas carry exactly one entry, or none under "ohne Login". The writer REFUSES to overwrite (`ManifestError` code `manifest-exists`) — that is a correct refusal, not a failure to work around. 4. **END the run with a hint, not an error.** Name the file written and the three things only a person can supply: `journeys[]` (name, persona, start, `steps[]` as `agent-browser` command lines, `success`, `max-steps`), `guarded-url-envs` (the env NAMES whose values must be loopback), and each persona's `goal`. Then stop. The first run's product is the manifest.

0.3 Load (manifest exists)

`loadManifest(

Read more
Ships withsession-orchestrator

Give your agents a working rhythm. Plan the work. Run it in checked waves. Pick up where you left off. Session Orchestrator is a free, MIT-licensed workflow plugin for Claude Code, Codex CLI, Cursor IDE, or Pi.

Get the whole plugin

Other skills on session-orchestrator.