Skip to content

qa-ui

qa-ui

qawriteshands-off

Use this when: a UI page needs its quality + test coverage checked

QA UI (quality + coverage)

The frontend safety net (epic #250): does this page render the real state, behave on every surface, and is that behaviour proven? The standard — the checklist, the craft tests, the report shape, the test-layer split — is docs/agents/qa-ui-contract.md. That doc is the source of truth; this skill is the how and does not restate it. Read it first, audit against it.

Scope — what's under audit

A selector: one page (Someday.vue), a feature (the task chat), or the changed UI (git diff main... -- resources/js). Default to the diff before a PR; take a named page when asked. Partition per-page — non-overlapping files — so a fan-out can't collide (the worktree rule).

The audit pass — this is the skill

Loading the contract + the page is setup; the value is turning each page into a pre-audit read then a gap list mapped to the layer whose proof is missing. Per page, in order:

  1. Pre-audit read — one line who / what / feel (page kind · audience · the calm design.md read), so a finding is judged against intent, not generic taste.
  2. Run the checklist (contract §"The checklist") — console cleanliness, render-correctness through the DTO layer, interaction/state-propagation across every derived view, observability (success/failure/empty), guideline conformance, surface coverage.
  3. Run the craft tests (contract §"Craft tests") — swap, squint, signature, token-drift. By eye; assert where mechanisable (token-drift greps for off-scale [#…] / shadow-[…]).
  4. Name the missing layer for each gap — Vitest (props→DOM), Pest feature (guard paths — the gate's teeth), or Pest browser (interaction + assertNoSmoke()
    • propagation). The contract §"Test-layer split" is the map; a guard path a browser test proves needs a feature-level twin or ci:check never sees it (the browser group is excluded from the gate).

The mechanics that make each layer deterministic — Agent::fake() for AI/tool flows, FakeTodoistClient::failGetTask() for partial outage, data-test hooks and attribute-selector counts — are in the contract; reuse them, don't reinvent.

Surface coverage — the page-surface manifest

resources/js/__tests__/page-manifest.test.ts fails ci:check for any Inertia page lacking a browser smoke test (unless grandfathered). A grandfathered page is itself a finding. To close one: write a real tests/Browser/<Page>Test.php (visits, renders the expected surface, assertNoSmoke() — a real assertion, never a hollow pass) and remove it from GRANDFATHERED in the same change, so the gate stays green. Never add a new page to the grandfathered set.

Output — surface, then fix

Per the contract §"Report + handoff shape":

  • Severity-tiered findings — Critical / High / Medium / Low, each with file:line.
  • Tag each real-gap vs subjective-preference + a confidence, so taste is triageable from defect. Name the cost before the severity word.
  • Surface-then-confirm — diagnose first; on confirm, apply the fix and write the missing tests. Never a silent UI rewrite.
  • A clean page is a valid result — say "passes" and stop.

Visual regression — out of scope here

The contract's tripwire is tripped, so a pixel-diff layer is recommended-forward and tracked as its own build — qa-ui does not build it. Until it lands, a pure-appearance gap (height-shift, badge styling) is reported as a real-gap the current layers can't cheaply assert, not silently dropped. Tooling choice lives in the contract.

Orchestration (opt-in)

When the gap-filling is large and disjoint — independent pages across non-overlapping files — fan out per-page across cheap-model workers per docs/agents/orchestration.md, then re-gate (ci:check) in-thread. A few pages, or gaps in shared files, stay in-thread.

Gate

composer ci:check green before the PR — including the page-manifest Vitest test and the smoke/Vitest/feature tests this pass added.

Where it sits

  • Not design-taste-frontend — that judges visual taste; this checks UI quality + assertable coverage against the contract.
  • Not qa-code (rule conformance on a diff) or qa-tests (backend ZOMBIES/mutation). It's the UI arm of qa-everything — fires when the change touches resources/js/** / a UI route.