How to prove General Translation work is done before saying so.
node scripts/install-skills.mjs gt-verify --project <dir>Run it from a Prototemplate checkout. It links skills/gt-verify into the project’s .claude/skills and .agents/skills; --copy vendors the folder instead, and --dry-run prints each step first.
Kevin checks General Translation (GT) work in the running app, and he finds what an agent did not look at. On 2026-09-02 he asked "did you actualy test this?" about a fix that had been reported with screenshots and never tried with a mouse. This skill defines when an item is done, which cases to walk for each kind of change, and what the report says about each item.
Capture tooling lives in gt-aesthetic (section 5, "Local review") and gt-website ("Looking at pages"), and the gates live in gt-lints. This skill adds the definition of done, the cases those tools miss, and two headless scripts: scripts/probe.mjs measures one spot of a page and scripts/stress.mjs walks a scroll story through the stress matrix. Commands and code for every check are in references/recipes.md.
$GT_CLOUD is a checkout of gt-cloud, GT's monorepo (the site at generaltranslation.com is its apps/landing, and the dashboard is apps/dashboard). $PROTOTEMPLATE is a checkout of Prototemplate, the design hub behind prototemplate.com. Run both scripts from $PROTOTEMPLATE, because its node_modules holds playwright-core. On a new machine, pnpm exec playwright-core install chromium webkit there downloads the browsers they launch. Pass a scratch folder as --out.
An item is done when all seven hold:
A typecheck, a clean lint and a passing structural audit are gates (gt-lints). They and the agent's own claim say nothing about what renders or what a user can do, so an item that passed only gates is reported as unverified.
sh node skills/gt-verify/scripts/probe.mjs http://localhost:3005/brand --sel ".pt-book-title h1" --theme dark --out <scratch>/probe
The probe prints the rect, each side of margin, border and padding, the content box, layout, type and paint values, the effective opacity, the parent's display and gap, and the distance to the previous and next siblings with the margins that make it up. It saves a crop of the element plus 24px at the device scale.
getComputedStyle on the live element (the probe, or the Browser pane's JavaScript) says what rendered.--dsf 4 for 4x). One 1px line is 2 device px at 2x, and a full-page capture hides it. The line auditor reads computed CSS and cannot see SVG strokes, so figures are judged on crops (gt-lints).--point x,y lists the elements under a viewport point from the top (elementsFromPoint), with each one's borders, background, opacity, shadow and outline, and the composite pixel there. It shows which owner draws a line, what covers a control and which layer leaks a fill.--scan x,y,down,24 (or right) groups the device pixels along a segment into runs of one grey, prints each run's width in device pixels and marks the runs that are lines. Compositing gives a*L + (1-a)*G for a stroke of alpha a and color L over ground G, and two stacked strokes composite at alpha 1-(1-a)^2. Expected greys in Prototemplate's shell, from src/components/viewer/tokens.css on 2026-10-05:| Line | One stroke | Two strokes | | --- | --- | --- | | Shell --pt-hair, dark (242 at 0.22 over 7) | 59 | 99 | | Shell --pt-hair, light (7 at 0.18 over 255) | 210 | 174 |
A reading near the two-stroke value is a double line or one stroke over a brighter backing such as a shader glow. A glow raises the neighbouring pixels as well, and a double line leaves them at the ground. On 2026-10-05 a scan read 59 across the toolbar's bottom rule on the brand page in dark (58 in WebKit), which is one stroke. The landing's main hairline --tc-hair is opaque (rgb 39, 39, 42 in dark and 228, 228, 231 in light on 2026-10-05), so its grey stays the same when two rules touch. There a double line shows as a run twice as wide: 4 device pixels at 2x for two 1px rules.
--frames 12 --every 120 takes the box reading and a crop twelve times. On 2026-08-01 an agent equalized the gaps between the locale words on a hero orbit and judged them from a capture at rest. Kevin then found a wider gap on the orbit's flank, where one word had rolled over and its neighbour had not yet, because each word's footprint changed with its roll state.pnpm check:pages runs a layout-shift observer on every page at four viewports, and stress.mjs records the entries in every phase. Around one gesture, read the entries directly (recipes, "Layout shift around one interaction"). Every entry over 0.001 is a defect, reported with its sources. Kevin, 2026-08-05: "either trannsition and animate them properly or dont let them layout shift around".gt-aesthetic section 5 and gt-website cover the external playwright-core harness, theme seeding, the scroll-through before a full-page capture, and the in-app pane that pauses requestAnimationFrame and blanks canvases. A blank or wrong capture is a tool problem to solve. Kevin is never asked to check what an agent can measure.getSelection(). A hover state is hovered, then clicked. A sign-in change is proven by signing in: "hmm but i cant sign in. full flow is technically not working" (Kevin, 2026-07-21). Recipes has the drag.click({ force: true }), a script that sets state, and a harness route in place of the real screen. On 2026-09-02 the label fix in gt-cloud #4633 was first shown with a Range API selection on a harness route. After Kevin's question, a real drag selected "Project name" on the fixed label and selected nothing with select-none put back. Report the shortcut, run the gesture, then report the gesture. A harness route stays out of the PR (gt-ship section 3); that session deleted it before committing..agents/skills/gt-dashboard "UI Verification Checklist" has Kevin confirm that nothing shifts on refresh or resize, small screens work, nothing flickers or refetches, loading and empty states hold still, focus and hover states show, transitions have no white flash and no text clips. Walk every line first with evidence (captures, stress.mjs shifts, the probe), then ask him to confirm with that evidence attached. Authenticated pages run on the per-worktree dev environment with its seeded session (gt-local-dev section 4), so a sign-in wall is no reason to leave a screen unverified. The /dev/states gallery mounts every auth-adjacent state; it lives on a branch outside main, and gt-local-dev section 4 says how to use it.gt init and gt configure wait forever on an unanswered interactive question, so every step runs with --no-interactive and a flag for each answer. Record whether the run used an existing CLI login, which skips steps a new user meets. gt-website section 6 and its references/pages.md hold the verified command.Scroll stories, pinned stages and animated sections are verified through the whole matrix before they are called fixed. On 2026-08-08 Kevin found three failures in one evening: "if i reload i briefly see 3 layers stacked on top of each other", a story that did not lock "in the same place" when scrolled up and down, and text rewriting that looked "so off" under 10x CPU throttling.
| Case | What must hold |
|---|---|
| Every beat, the last ones included | Each beat, the finale included, locks at its read line |
| Slow, fast and reverse scrolling | The same position shows the same state from either direction; nothing stale is visible after a fast jump |
| Reload mid-page and a restored scroll position | The first painted frame is already right: no stacked layers, no hidden copy |
| A deep link into the story | The linked beat shows lit, with no extra scroll needed |
| Zoom fully out and back in | The story at the same position reads as it did before |
| Show and hide the mobile URL bar | The document does not jump (DESIGN.md section 13) |
| CPU throttled 10x | Settled states equal the unthrottled run; only the frame rate drops |
| 1440 by 900, 1920 by 1080, intermediate widths, a phone | Each width shows one composition (section 4) |
node skills/gt-verify/scripts/stress.mjs http://localhost:3001/en-US \
--copy ".v0-stack-beat" --story "#platform" --hash platform --theme dark \
--out <scratch>/stressREPORT.md and index.html, a sheet of every capture. --phases, --viewports and --steps narrow a run; --each requires every element in the band to be visible.--hide "<css>" to read the story under it.gt-motion holds the motion rules (one clock, paused loops, reduced motion). gt-landing-pages and DESIGN.md sections 13 and 14 hold the story's layout (the 55 and 80 percent read lines, the svh and dvh law).pnpm check:pages --pages <ids> reads ten viewports (360x800 to 2560x1440) in both themes, layout shift at four viewports and the declared interactions (docs/SHIP-LOOP.md section 2, gt-lints). For a site outside Prototemplate, pass --base <url> --pages-module <file> (prototemplate section 10). gt-aesthetic adds shots at 1440 and 390 in both themes and plate pages at 1527 by 814.items and columns (gt-website section 2).--browser webkit on both scripts. On 2026-08-18 the blog index's lead card broke only in Safari: WebKit resolved the cover's height: 100% against the whole two-row subgrid. The fix was verified with one invariant in Chromium and WebKit at desktop and tablet widths.--theme), and confirm it took: the probe prints data-theme and the dark class.sh node skills/gt-verify/scripts/probe.mjs http://localhost:3001/en-US/docs --viewport 390x844 --a11y --strict --out <scratch>/a11y
It lists controls with no accessible name, the name every icon-only control announces with its aria-expanded or aria-pressed state, text set to user-select: none, and the count of live regions. Read each announced name against what the control does. On 2026-09-02 the docs drawer trigger had no name, the drawer's close button announced "Open Sidebar", and dashboard labels were select-none. State changes are announced (aria-expanded and aria-pressed on toggles, a live region for an async result). Tab through the changed controls and see the focus ring. Reduced motion renders the designed still (gt-motion).
/dev/states gallery (gt-local-dev section 4) supply the states.dir on the node, unicode-bidi: isolate), and Arabic stays joined, which needs one shaped text node with no per-character spans (DESIGN.md section 8). Devanagari keeps its matras. Every layout is checked in CJK, RTL and Indic text as well as Latin (BRAND.md section 6).git log and git diff <good> <bad> -- <paths>) or bisect in a scratch worktree before changing code (recipes).rm -rf .next clears a stale dev build (gt-website section 2).uptime load average beside every timing and rerun at low load before calling a timing defect real (gt-performance section 2, which also holds budgets and frame-time work).Work that shipped is done when production serves it. Kevin, 2026-10-01: "why does your deploys keep regressing?"
git merge-base --is-ancestor <sha> origin/main succeeds; a lane's work lost in a rebase counts as unshipped.githubCommitSha and aliases with vercel api /v13/deployments/<url>, and check that it contains your merged commit (recipes, "The live deployment"). A newer deployment still building serves nothing yet.dpl_ id. On 2026-10-05 that id matched the newest Ready production deployment, built from origin/main's e17fce499. Prototemplate's check is gt-ship section 8.dpl_ id is older than the deployment is a stale copy.200 image/png, and the card is opened and looked at (recipes; gt-graphics for the design).gt-reporting section 2). gt-website section 8 reads the landing's Vercel logs; gt-ship section 8 reads Prototemplate's.When Kevin flags one instance, find every instance: "in everything, make sure our footers are properly logo and text side by side" (Kevin, 2026-08-03); "look for ANYWHERE ELSE icons nneed to be synced" (Kevin, 2026-09-28).
git grep -n "select-none" -- 'packages/ui/src' 'apps/*/src'), a registry (Prototemplate's scripts/site-pages.mjs, the header's items and columns), a lint (gt-ui rules), or a measurable heuristic such as symmetry (left and right padding equal) or attachment (a logo and its wordmark on one baseline) read with the probe on every member (--nth).select-none from the shared Label primitive in packages/ui, which every label above every dashboard input renders.gt-lints section 4). Cross-app UI changes go through packages/ui (gt-components, "Standardization").gt-testing, "Tautological").REPORT.md, the text in the response) as well as the exit code. Gate hygiene (chained with &&, unpiped, real exit codes) is gt-lints section 5.gt-ship section 3 and gt-testing hold the size rules.ALLOW entry or a baseline row with its reason) that keeps the failure visible. A gate is never skipped silently.--pages, --viewports, --phases); the full matrix before a release. Run browser gates one at a time.gt-local-dev section 4). A CLI login under test runs with XDG_STATE_HOME=<scratch>. The gt CLI keeps its sign-in state under that folder, so his stored login stays untouched (gt-local-dev section 5). A server Kevin started is never stopped (gt-local-dev section 6).xcrun simctl boot opens no window; the simulator panel opens only when Kevin wants to watch.Every reported item says how it was verified and names anything left unverified, with the reason. gt-reporting holds the report format; gt-aesthetic and gt-ship hold the crop format.
The 2026-09-02 report on the label fix asked Kevin to sign in and drag across a label himself. To this standard the agent runs that drag on a signed-in screen through the seeded session, and the report reads: "Labels above inputs select again (#4633). Verified: on a signed-in dashboard form, a mouse drag selects 'Project name' after the fix and selects nothing with select-none restored; the shared Label primitive computes user-select: auto. Not verified: Safari on an iPhone. The simulator device still needs Kevin's one-time grant, and WebKit at 390x844 passed."
stress.mjs in every phase, and the first-frame captures were looked at.probe.mjs --a11y --strict.dpl_ stamp matches, each feature was spot-checked live, and the cards were read live.Prototemplate: gt-aesthetic (the review standard and local review tooling), gt-website (the site, its deploys and "Looking at pages"), gt-lints (every gate, gate hygiene and new lints), gt-ship (PR crops, Prototemplate's deploy stamp), gt-motion (motion rules and verifying motion), gt-landing-pages (scroll story layout), gt-components (cross-app standardization), gt-graphics (social cards), gt-reporting (the report format), gt-local-dev (dev environments and review servers), gt-performance (frame budgets and Lighthouse), prototemplate (check:pages against another site). gt-cloud: gt-dashboard (the UI Verification Checklist), gt-testing. Kevin's wiki: agent-browser, webapp-testing, dogfood, accessibility, og-metadata-audit, agent-iteration-loop.
scripts/pagecheck/README.md and pagecheck.mjs; scripts/site-pages.mjs; src/components/viewer/tokens.css (the hair tokens); skills/gt-aesthetic, gt-website (with references/pages.md), gt-lints, gt-ship, gt-motion, gt-landing-pages, gt-components, gt-local-dev, gt-performance, gt-reporting and prototemplate; all read 2026-10-05..agents/skills/gt-dashboard/SKILL.md (UI Verification Checklist); .agents/skills/gt-testing/SKILL.md; apps/landing/gt.config.json; packages/ui/src/components/ui/label.tsx; PR #4633 (open) and PR #4240 (merged 2026-08-08).packages/cli/CHANGELOG.md (#2205, sign-in state under $XDG_STATE_HOME/gt).vercel ls landing and vercel api /v13/deployments for the newest Ready production deployment, the dpl_ stamp and OG tags of generaltranslation.com, the landing's computed --tc-hair in both themes, stress.mjs and probe.mjs runs against generaltranslation.com and localhost:3005 in Chromium 153 and WebKit 26.5.gt-local-dev carries the parts of them this skill relies on.The folder skills/gt-verify as agents fetch it. Each file opens raw.
SKILL.mdreferences/recipes.mdscripts/probe.mjsscripts/stress.mjs