Skip to content

Commit 4c51ed1

Browse files
chore(mosaic): add the verify-mosaic skill for browser checks in swingset (#10542)
1 parent 79138c0 commit 4c51ed1

7 files changed

Lines changed: 979 additions & 0 deletions

File tree

‎.changeset/verify-mosaic-skill.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
---
2+
---
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
---
2+
name: verify-mosaic
3+
description: Drive @clerk/mosaic components and flows in swingset, the Mosaic component explorer (Next.js dev server in packages/swingset), through an isolated agent-browser session, and capture ARIA snapshots, screenshots, and DOM attribute dumps as evidence. Use it to prove a change to packages/mosaic or packages/swingset works in a real browser before calling it done, to reproduce a Mosaic UI bug, or to check knobs, overlays, RTL, chaos data, and dark mode on a component page.
4+
---
5+
6+
# verify-mosaic
7+
8+
`bin/control-swingset` (beside this file) starts swingset for this worktree, checks it, gives you an `agent-browser` session that no other worktree shares, makes evidence folders, and tears it all down. Swingset reads Mosaic from `packages/mosaic/src`, so a Mosaic edit shows up on reload without rebuilding `@clerk/mosaic`.
9+
10+
A change to Mosaic UI is not done until a browser shows the changed behaviour on the page that documents it. Unit and browser-mode tests in `packages/mosaic` prove the code. This skill proves what a user sees.
11+
12+
Run every command from the repo root. Below, `CS` means `packages/mosaic/.claude/skills/verify-mosaic/bin/control-swingset`.
13+
14+
## Launch
15+
16+
Once per machine: install `agent-browser` 0.27 or newer (`brew install agent-browser`). Read `agent-browser skills get core` once if you have not used it.
17+
18+
Once per worktree, after `pnpm install`:
19+
20+
```console
21+
$ pnpm turbo build --filter='@clerk/swingset^...'
22+
$ CS up
23+
start pid 3722 log <repo>/packages/mosaic/.verify/swingset.log
24+
listen http://localhost:6008
25+
ready http://localhost:6008/components/button session verify-mosaic-057349b4
26+
```
27+
28+
- `up` runs `packages/swingset/scripts/dev.mjs`, which takes port 6006 or the next free one. Other worktrees often hold 6006 and 6007, so always use the port `up` prints, or `CS url`.
29+
- Ready means `/components/button` returned 200, which forces the first compile (about 20s cold).
30+
- `up` reuses an instance this worktree already started. It refuses when a swingset that it did not start is running in this worktree, because the two would share `packages/swingset/.next`.
31+
- `up` refuses when `@clerk/nextjs` or `@clerk/shared` has no `dist/`, and prints the build command.
32+
33+
## Doctor
34+
35+
Run it first, and again whenever anything looks off. It only reads.
36+
37+
```console
38+
$ CS doctor
39+
ok agent-browser 0.27 or newer (found 0.27.0)
40+
ok state file <repo>/packages/mosaic/.verify/instance.env
41+
ok process 3722 alive
42+
ok port 6008 served from this worktree (<repo>/packages/swingset)
43+
ok http://localhost:6008/components/button returns 200
44+
info started at HEAD 9f579d368f (now 9f579d368f)
45+
info session verify-mosaic-057349b4
46+
```
47+
48+
Each `FAIL` line has a `fix`. The exit code is 1 when any check fails. Swingset reloads source edits, so a different `HEAD` is only information. After a dependency rebuild, run `down` and `up`.
49+
50+
## Drive
51+
52+
`CS ab <args>` is `agent-browser --session verify-mosaic-<hash>` for this worktree. Never call bare `agent-browser`: its `default` session can belong to another agent.
53+
54+
```console
55+
$ CS ab open "$(CS url /components/dialog)"
56+
$ CS ab snapshot -i # interactive elements with @refs
57+
$ CS ab find role button click --name "Open dialog"
58+
$ CS ab wait '[role=dialog]'
59+
$ CS ab eval '(() => document.querySelector("[role=dialog]").dataset.variant)()'
60+
```
61+
62+
Conventions:
63+
64+
- Routes are `/<group-slug>/<component-slug>`: `/components/button`, `/components/dialog`, `/user-button/user-button`, `/misc/organization-switcher`. The slug is the story's `meta.group` and `meta.title` run through `packages/swingset/src/lib/slug.ts`. A wrong path still returns 200 with `No docs found`, so check the level-1 heading. Read the paths of the expanded sidebar groups with `CS ab eval '(() => [...document.querySelectorAll("[data-sidebar] a")].map(a => a.getAttribute("href")))()'`, or click the link with `CS ab click 'a[href="/user-button/user-button"]'`.
65+
- Prefer role and accessible name (`find role button --name …`). When `find` cannot resolve a name, as with the header switches, take `snapshot -i` and use the `@ref`. Refs change after every navigation and every re-render, so take a new snapshot first.
66+
- Mosaic parts carry their identity in the DOM: a `.cl-<slot>` class plus `data-<axis>` attributes (`data-variant`, `data-size`, `data-color`, `data-open`). Assert on those, not on computed colours or class hashes. `data-style-src` lists the StyleX source lines that styled the element.
67+
- Wrap every `eval` body in an IIFE. The page keeps `const`s between `eval` calls, and a second `const b` throws.
68+
- Wait on an element (`CS ab wait '<selector>'`), never on `wait --url`. Swingset navigates on the client, and `wait --url` hangs until its timeout.
69+
- `agent-browser` has no per-command timeout. Give the Bash call its own timeout.
70+
- If a command hangs or prints `CDP response channel closed`, run `CS ab close`, then `CS doctor`, then start the recipe again from `open`. Doctor checks the server, not the browser, so a hung browser still shows all `ok`.
71+
- Set the viewport with `CS ab set viewport <w> <h>` before `open`. Changing it on a loaded page can drop the browser connection.
72+
- When you pull a ref out of a snapshot with `grep`, match `e[0-9]\+`. A bare `e[0-9]*` also matches the `e` in `ref`.
73+
74+
Read [`notes.md`](notes.md) before driving. It says what to prove for a primitive, a component, a feature, or a Live Sandbox change. When a note turns out to be wrong, fix it in the same PR.
75+
76+
## Evidence
77+
78+
```console
79+
$ E=$(CS evidence dialog-open) # packages/mosaic/.verify/evidence/<utc>-dialog-open
80+
$ CS ab snapshot > "$E/open.aria.txt"
81+
$ CS ab screenshot "$E/open.png"
82+
$ CS ab eval '(() => Object.fromEntries([...document.querySelector("[role=dialog]").attributes].map(a => [a.name, a.value])))()' > "$E/open.attrs.json"
83+
```
84+
85+
Proof standards:
86+
87+
- Drive the user path: click, type, and press keys on the page. Do not set React state or call story functions from `eval`. Use `eval` only to read.
88+
- Capture the action and the state it produced: before and after for a toggle, open and closed for an overlay, the knob value and the preview attribute for a prop.
89+
- Prove the check can fail. Save your diff to a file, undo the change, confirm the undo took effect, run the same commands into a `CS evidence <feature-id>-control` folder, and show the result differs. Then apply the diff again and run the check once more. A check that passes with and without the change proves nothing.
90+
- A screenshot alone proves nothing about state. Pair it with the ARIA snapshot or the attribute dump that shows the claimed value.
91+
- For a styling change, check both themes (`Dark mode` switch) and RTL when the change touches layout.
92+
- Name the feature file and entry point you used. Report every entry point you could not reach, and why, instead of claiming it through another one.
93+
- Evidence lives in `packages/mosaic/.verify/evidence/`, which git ignores. `CS down` keeps it. Each folder's `source.env` records the commit it was made at, whether the tree had uncommitted changes, and whether swingset had an env file loaded.
94+
95+
## Attach
96+
97+
Once the branch is pushed and has a pull request, put the screenshots in its description:
98+
99+
```console
100+
$ CS attach "$E" --pr <n> --before "$CONTROL"
101+
attached 3 screenshots with befores to https://github.com/clerk/javascript/pull/<n>
102+
```
103+
104+
- It writes a block between `<!-- verify-evidence:mosaic -->` and `<!-- /verify-evidence:mosaic -->`. A line names each evidence folder with the commit it was captured at, and the pull request's head commit when it was attached. With `--before`, a table shows each control screenshot beside the screenshot of the same name. Without it, the screenshots sit two across under their names.
105+
- Run it again to replace the block. Nothing outside the markers changes.
106+
- The upload is public, so it takes only fixture data. It refuses a folder that `CS evidence` did not make in this worktree's evidence folder, and a folder captured while swingset had an env file loaded, such as `.env.local` for the Live Sandbox. It also refuses while the running swingset has loaded one.
107+
- It refuses when a marker appears twice or alone, when the text between the markers does not start with its `**Verified in swingset**` line, or when the description ends in an unclosed code fence. Markers inside a code fence do not count.
108+
- It edits the description twice: once to upload the screenshots as a list, then to lay them out. If it stops after it began uploading, even when only some uploads failed, it says so. Check the description, then run it again.
109+
- `--before` keeps only the screenshots both folders have, and names each one it skips. Save the control screenshots under the same names.
110+
- It needs `gh` 2.99 or newer for `gh pr edit --attach`. `--dry-run` prints the command without changing the pull request.
111+
- Only `.png` files go up. ARIA snapshots and attribute dumps stay local; quote the line that proves the claim in the description instead.
112+
113+
## Cleanup
114+
115+
```console
116+
$ CS down
117+
closed browser session verify-mosaic-057349b4
118+
stopped pid 3722
119+
kept evidence in <repo>/packages/mosaic/.verify/evidence
120+
```
121+
122+
`down` closes only this worktree's browser session and stops only the process `up` started, after checking that the process runs from `packages/swingset`. It never kills by process name. Run it after a failed attempt too. A failed `up` runs it for you.
123+
124+
## Changing the script
125+
126+
`bin/test-control-swingset` tests `bin/control-swingset` without starting swingset. Run it after every change to the script, and add a case that fails without your change.
127+
128+
## Not covered
129+
130+
- The Live Sandbox (`/live`, `/sign-in`, `/sign-up`) needs `packages/swingset/.env.local` with a publishable key. Without it, report Live Sandbox paths as skipped. See [`notes.md`](notes.md).
131+
- The Playwright suite for Mosaic in a real Next.js app (`pnpm test:integration:mosaic`, tests in `integration/tests/mosaic/`) needs the team's integration secrets. This skill does not run it.

0 commit comments

Comments
 (0)