Tours

Scripted browser walks through the running app that produce review artifacts for rendering and accessibility review.

What it is

A tour is a scripted walk through the running app that produces review artifacts: screenshots at desktop (1280×800) and mobile (375×800), accessibility-tree snapshots, axe-core violation reports, and any soft-assertion findings the tour author wrote. Tours live in test/tours/*.tour.ts; the framework is test/tours/tour-lib/.

A tour is the app's walk, written down. Writing the walk down is more solid than doing it by hand: the next person gets the walk instead of reinventing it. That value lasts only while the walk still runs, so a weekly session runs every tour and keeps it true (schedules/tour-check/). When a tour misses because the app changed deliberately — a landing on main, a plan, or a filed issue explains the new label or structure — the session edits the tour to describe the app as it now is. It fixes missing or ambiguous control names itself. Anything else it finds (a miss nothing explains, another axe violation, a page error, visible breakage in a screenshot) it files as an issue and reports. Its report is the one thing that has to be read: a walk nobody runs rots, and a report nobody reads is the same failure one level up (three of five tours had been silently failing for months before this existed — 2026-08-26). That asymmetry with the smoke review, which files and never edits, is by design.

Tours are not a test gate. Findings and axe violations never affect the exit code, artifacts are gitignored, and neither CI nor pre-commit runs them. A gate has one response to intended UI change — go red — so a soft-finding instrument makes a permanently red gate that gets ignored; the weekly session asks the other question, drift or new truth?, which a gate cannot. Behavior belongs in tests; "does the app boot at all" is the smoke walk, a merge gate on the same browser library with different failure semantics, deliberately not the same walk.

When to use tours

When not to use tours

Running it

bin/tour              # list available tours
bin/tour capture      # run one
bin/tour --all        # run every tour

Needs the shared dev router (pnpm dev at the monorepo root) reachable; the worktree is auto-detected from $PWD, box defaults to test1 (BROWSE_BOX overrides). The box must be one built for agent browsing — "agentBrowsing": "owner" in its _config/box.json, which test1 and its clones carry — or every owner-gated surface walks as a 401/403 page (docs/implemented-plans/agent-browsing-owner.md). A healthy tour takes tens of seconds — both viewport passes included.

Tours use named per-session Chrome profiles, so a tour can run alongside an interactive bin/browse session in the same worktree. Keep a tour's own session name stable when debugging its walk; different sessions have separate cookies and tabs.

Each tour runs as two full passes (desktop, then mobile). Expectations are soft — a miss records a ❌ finding and the tour continues; only a thrown error (unresolvable locator, browser failure) aborts a pass, and even then the other pass still runs and reports.

Writing one

API sketch

tour({ name, description }, async (t) => {
  await t.go("/dashboard");          // path → dev-router URL
  await t.checkpoint("loaded");      // screenshot + AX + axe, both viewports
  await t.expect.heading("Dashboard", { level: 1 });  // soft assertion
  await t.expect.landmark("Primary");
  await t.expect.noPageErrors();
  await t.click({ role: "link", name: /^Browse/ });   // accessible-name locator; string = exact, RegExp = match
  await t.expect.custom("has rows", (ax) => ax.includes("row"));
});

Reading results

Each run writes test/tours/.artifacts/<tour>/<runId>/ (gitignored):

A capture-time degradation (page-readiness timeout, axe crash) is recorded as a ⚠️ finding rather than silently producing a loading-state screenshot or a fake "0 violations".

How an agent reviews with tours:

  1. bin/tour <name>; note the console counts.
  2. Read summary.md — findings first.
  3. View the checkpoint PNGs directly (agents can read images): compare desktop vs mobile, look for clipped/overlapping/empty states, and report any visible breakage even when tangential to the task at hand.
  4. Where a checkpoint shows nonzero axe violations, open its .axe.json for the nodes and failure summaries.
  5. Treat ❌ findings and visible breakage as work items; ⚠️ capture degradations mean the artifact itself may be unreliable — re-run before drawing conclusions from it.