Reproducible Rendered UI Validation

Use the rendered UI harness when a Swarm Hosts change depends on what a user actually sees or can operate in a browser. It starts temporary control-plane and admin applications, seeds a deterministic synthetic world, logs in without real credentials, and writes sanitized evidence outside the repository.

The default acceptance profile renders these states at exactly 1440x900, 1280x800, 768x1024, and 390x844:

  • Minecraft creation;
  • large and empty deployment lists;
  • healthy and failed resource details;
  • degraded/offline status;
  • owner, manage/operator, view-only, resource-admin, and light-theme permissions.

The fixture also contains a platform admin, an empty account, online Linux and Windows hosts, an explicitly offline host, pending and disabled actions, long names/paths/logs, 14 deployments, Minecraft, Terraria, and nine other supported game types. IDs are stable. Time-relative fields share one run anchor so they do not decay independently during a sweep.

Run the same profile locally or in the factory

Reuse the repository virtual environment. Install the pinned browser-side dependency and Chromium once if they are not already present:

npm ci --prefix scripts/validation/browser --ignore-scripts
.venv/bin/python -m playwright install chromium

Commit the candidate first. Durable evidence fails closed when the worktree is dirty because each manifest must name the exact tested commit. Export GITLAB_ISSUE_IID and GITLAB_MR_IID with the actual positive numeric IIDs for the candidate under review. The required-value expansions below deliberately stop a copied command before capture if either value is missing; never reuse provenance from another issue or merge request.

make validate-rendered-ui \
  UI_ISSUE="${GITLAB_ISSUE_IID:?Export GITLAB_ISSUE_IID as the actual issue IID}" \
  UI_MR="${GITLAB_MR_IID:?Export GITLAB_MR_IID as the actual MR IID}" \
  UI_REVIEWER=implementation-worker:skitter \
  UI_TEST_RESULT='make test-fast=passed' \
  UI_RENDER_ARGS='--allow-findings'

--allow-findings changes only the process exit code so an implementation worker can retain evidence of an existing UI defect. The manifest and affected capture remain passed: false; no finding is hidden. An independent reviewer should omit that option and provide --visual-verdict pass or --visual-verdict fail after inspecting the screenshots.

Use the inventory profile for the complete route/state matrix:

make validate-rendered-ui \
  UI_ISSUE="${GITLAB_ISSUE_IID:?Export GITLAB_ISSUE_IID as the actual issue IID}" \
  UI_MR="${GITLAB_MR_IID:?Export GITLAB_MR_IID as the actual MR IID}" \
  UI_REVIEWER=independent-validator \
  UI_TEST_RESULT='make test-fast=passed' \
  UI_RENDER_ARGS='--profile inventory --visual-verdict pass'

Use repeatable --target TARGET_ID or --viewport NAME=WIDTHxHEIGHT arguments for focused diagnosis. The default acceptance profile always uses all four contract viewports. Target definitions come from docs/design/ui-ux-route-state-inventory.json; do not add a second hard-coded route list.

What the harness records

Every capture includes:

  • the target ID, surface/base URL placeholder, route and query/modal/drawer state;
  • role, fixture, game type, data state, GitLab issue/MR, exact commit SHA, browser/version, viewport, and UTC timestamp;
  • viewport and full-page PNG paths, byte lengths, SHA-256 hashes, and sanitation status;
  • top-level status, route preservation, console errors, page errors, Playwright requestfailed events, and first-party document/XHR/fetch/asset failures;
  • page-level horizontal overflow plus separate deliberate terminal/log/code scroll containers;
  • axe-core WCAG A/AA critical/serious results and computed color-contrast classification;
  • explicit Tab order, viewport visibility, and computed focus-indicator results;
  • form validity gating and payload-key checks for the Minecraft create flow;
  • the evidence producer, visual verdict, approved exceptions, findings, and test command/results.

The harness captures both viewport and full-page screenshots at every viewport, so mobile content below the first fold is never omitted. Pixel differences and DOM metrics are regression signals, not aesthetic approval.

Evidence manifest contract

The artifact directory contains summary.json with schema swarmhosts_rendered_ui_evidence/v2. Required top-level sections are:

Section Contract
provenance Positive GitLab issue/MR IDs, exact 40-character commit SHA, and sourceDirty: false.
browser Browser name/version plus pinned axe-core version and source hash.
inventory Inventory schema/path, total surface count, selected targets, and run profile.
fixture Fixture profile, safe role/state/game coverage, and dynamic key names without token values.
sessionProfiles Every guest/auth role and authStorage: memory-only.
acceptanceCriteria Acceptance criterion IDs mapped to target IDs.
viewportMatrix The exact desktop-wide, desktop-standard, tablet, and mobile dimensions.
tests At least one exact command/result pair supplied by --test-result.
review Reviewer identity and explicit visual verdict.
approvedExceptions Optional check code and reason pairs; unapproved findings remain failures.
results One complete record for each unique target/viewport pair.
manifestValidation Parser result; any missing field, unsafe path, bad hash, dirty source, or sanitation failure rejects the manifest.

Each result must contain both screenshot kinds, valid SHA-256 hashes, passed sanitation, console/network/accessibility/keyboard/route results, a visual verdict, and its applied exceptions. Parser and hashing behavior is covered by tests/test_rendered_ui_validation_contract.py.

Artifacts must remain under /tmp or another path outside the repository. Browser storage state is kept in memory, reset/Discord tokens are never written to the manifest, synthetic sensitive inputs are redacted before screenshots, and no real account or deployment is contacted.

Existing tooling and migration

This extends rather than replaces the existing tools:

  • scripts/validation/rendered_ui_validation.py is the inventory-driven implementation/review evidence path.
  • scripts/validation/ui_density_evidence.py remains the focused before/after collector for compactness and information-density changes.
  • scripts/validation/final_ui_route_sweep.py remains a compatible 33-route historical gallery entry point, but its v2 output no longer hard-codes issue 238 and its authentication state stays in memory instead of the artifact directory.

Do not approve accessibility based only on color or DOM inspection. The axe-core scan and keyboard/focus traversal are both required, and an independent visual verdict is still necessary for layout or aesthetic claims.