ui-verification · git:20261005.c1ba25d · 2026-10-05 · sha256 44a63651d6e926a7
ui-verification git:20261005.c1ba25dA
Immutable. This exact content never changes and is served at /api/v1/blob/44a63651d6e926a7.
--- description: UI layout checks before claiming work is done globs: external/ui/**/* alwaysApply: false --- # UI verification (Playwright MCP) Before you tell the user that a UI change is complete or merge-ready: 1. Choose one free port (for example **`PORT=5201`**) and start **`npx vite --host localhost --port "$PORT"`** in **`external/ui`**. 2. Use **Playwright MCP** (or the repo browser tools) to open **`http://localhost:$PORT/layout-scroll-check.html`** and **`http://localhost:$PORT/`** with at least two viewports: **narrow** (for example **390px**) and **wide** (for example **1280px**). 3. Run **`browser_evaluate`** to compare **`getBoundingClientRect()`** edges for **`.chat-header`**, **`.messages-inner`** first child, and **`.composer-card`** (left and right should match within about **1px** after shared column padding). 4. If anything is off, fix CSS and re-check before reporting done. This is required for changes to **`external/ui/src/styles.css`**, **`ChatScreen`**, **`Composer`**, or nav layout. ## Safari and other WebKit reports A Safari-only report is reproducible without a Mac: Playwright's WebKit is the engine Safari is cut from and its version tracks Safari's. When the change touches a capped, scrollable or **`position: fixed`** surface, run **`external/ui/scripts/webkit-scroll-check.mjs`** against a live **`coddy serve`** (setup and env vars in **`docs/surfaces/web-ui.md`**, *Reproducing a Safari report without a Mac*), and run it again with **`CODDY_ENGINE=chromium`** to tell a WebKit-only regression from a layout bug every engine shares. ## The transcript at every width of the grid Nothing in the transcript may be wider than the transcript: on the stacked shell its column is the page, and one row that cannot wrap makes a phone scroll sideways (**`DESIGN.md`**, *Layout grid*, *Tool card UI*, *Markdown*). When the change touches the transcript's rows, the Markdown styles or a width query, run **`external/ui/scripts/phone-overflow-check.mjs`** against a **`vite`** dev server (setup in **`docs/surfaces/web-ui.md`**, *Checking the transcript at every width of the grid*): it mounts the rows that used to overflow and fails when the page scrolls sideways or anything sticks out of **`.messages-inner`** at 360 to 1280px. A new width query goes into the grid first; the vitest **`layoutGridCss.test.ts`** fails on one the grid does not name. ## The fold chevron The chevron is an SVG whose ink is centred in its viewBox, never a text glyph: a glyph's ink moves with the platform's font, which is why the reports of a chevron riding above its label kept coming back (**`DESIGN.md`**, *Chevron*). jsdom has no layout, so vitest cannot see where it lands. When the change touches the chevron, the rows it sits on or the type around them, run **`external/ui/scripts/chevron-align-check.mjs`** against a **`vite`** dev server (setup in **`docs/surfaces/web-ui.md`**, *Checking the fold chevron against its label*): it measures the chevron's ink centre against the label's on a transcript row and on the Tasks panel toggle, and fails past **1px**. ## A long transcript Only a bounded slice of a transcript is in the DOM, and a long session holds only the end of its history until the reader scrolls up (**`DESIGN.md`**, *Transcript window*; **`docs/surfaces/web-ui.md`**, *Long sessions*). jsdom has no layout, so vitest renders every row and cannot see the window at work. When the change touches the transcript window, the rows, the messages or tool-calls routes, or what a row renders, run **`external/ui/scripts/transcript-window-check.mjs`** against a binary built with **`TAGS="http ui swarm"`** (setup in **`docs/surfaces/web-ui.md`**, *Checking a long transcript*): it opens a 3306-message session under CPU throttling, scrolls it without a jump, edits a prompt of an older page, runs a turn in a second browser, reads through a swarm relay, and fails past a budget. Run it once more with **`CODDY_ENGINE=webkit`** when the change touches scroll anchoring: WebKit does not anchor scrolling, so there the window's own correction is all that keeps the reader's row still. ## Capture isolation and redaction - Captures use a temporary browser-context page, never the operator's main tab, and close it afterward. - Navigate the capture page through localhost to avoid host-specific zoom. Set the viewport explicitly and blur focus unless focus is what the screenshot documents. - Screenshot stands use isolated `HOME` and `CODDY_HOME` with neutral fixtures. - Before pushing, inspect every PNG for skill names, hosts, tokens, usernames, and personal paths. - Screenshots for English pages and pull requests are taken with `?lang=en`, so the browser's locale does not decide the language of the capture.