An AI coding agent that has just changed your UI will tell you the change works. Ask it how it knows, and the answer reduces to: because I wrote it that way. Tests pass, types check, the build is green, so the button must do what the ticket says. Every clause in that answer is a claim about code. Your users never meet the code. They meet the rendered page, and a whole class of failures lives in the gap between the two.
The agent, in other words, is the only witness to its own work, and it is an interested one. The fix is to give it a browser. A browser-automation layer (Playwright is the one wired into my environment; Puppeteer and the browser tools built into agent products belong to the same class) turns the agent from an author defending its work into an operator observing it. The agent opens the real app, clicks the real controls, reads the real state back, and screenshots what it saw. The claim changes shape: not “it should work” but “I watched it work,” with the evidence attached.
What this looks like in my setup
My day-to-day agent is Claude Code with the official Playwright plugin enabled. The agent gets browser tools the same way it gets git or a shell: as callable operations, arriving over MCP, the protocol coding agents use to reach external tools. My global permission settings pre-approve those browser operations, a configuration I walked through in my guide to Claude Code permissions. The principle there was to pre-approve reads and observations while gating writes, and it matters here for a practical reason: a verification pass that stalls on an approval prompt every few clicks is a verification pass that stops getting run.
A pass itself is plain. I name the workflows that matter for the change: load this page, submit this form, confirm the redirect, check what renders when the query returns nothing. The agent drives each one in a live browser, reads back what the page actually shows, and captures a screenshot per step. Then I judge the evidence instead of the assurance, which in practice means looking at the screenshots rather than the summary of them.
Three places it has earned its keep
The suite the agent talked itself out of. In a December 2025 piece I told the story of wanting end-to-end tests for a tool I was building. The agent advised against it: a proper suite would mean installing Playwright or Puppeteer, and that would be overkill for the project. Overkill for whom? It was pricing the work at human cost, the three hours of configuration and troubleshooting the setup would have cost me by hand, when its own labor was the thing being spent. I told it to install Playwright and build the suite. Three minutes later I had end-to-end coverage of the critical paths, with setup, teardown, and assertions on actual behavior. The agent was right that the work was involved. It was wrong that this was a reason to skip it.
The committed gate. My personal site’s repository carries an audit script built on Lighthouse. It drives headless Chrome against four representative pages (the homepage, the blog listing, a recent article, a static page) and fails the run when any score drops below thresholds I set, the strictest being accessibility at 95. It has a CI mode that exits nonzero, so any pipeline that runs it fails loudly. Any session, mine or an agent’s, can run it with one command and read the scores it emits.
Eyes and hands beyond my own code. In May 2026, after a sitemap that broke during a site migration quietly cost my sites search visibility, the recovery ran through web consoles with no useful API for the operations I needed: Google Search Console, Bing Webmaster Tools, Cloudflare. I directed a single agent session through all of it with browser automation. It resubmitted sitemaps and read the status back, ran URL inspections and reported the indexing state of each page, and after the deploy re-fetched the sitemap, robots.txt, and a sample article on each site to confirm the contract was correct. That session ran on the agent’s built-in browser tooling rather than Playwright, which is the point: the discipline belongs to the tool class, not to one library. It wasn’t QA of code written that day, but verification of external state, the same discipline aimed at a different artifact.
What the browser catches that unit tests miss
Unit tests construct their own world: mocked dependencies, fixture data, inputs chosen by the same mind that wrote the code. They are necessary, and they verify components in isolation. The failures that reach users tend to be failures of composition, and none of these are exotic; every web engineer has shipped at least one. The component passes its tests and the route never mounts it. The form submits and the handler reads a field that was renamed last week. The modal renders under the overlay. Hydration dies silently and leaves a page that looks fine and does nothing. The API answered yesterday’s schema. A browser pass observes the composed system the way a user does, and that vantage point is the whole reason it catches this class.
The verification protocol in my open-source skills collection states the underlying rule: the gap between “tests pass” and “production works” is where the worst failures live, because everyone has stopped looking. A browser pass is a cheap way to keep looking.
Verification, not trust
Synthesis engineering centers on a division of labor: the human directs and verifies, the agent executes. The browser layer is what makes the verification half real for UI work. Without it, “done” is a self-assessment by the party that did the work. With it, “done” arrives as an evidence package: readbacks, screenshots, response bodies, score reports. I can audit a screenshot in seconds. I cannot audit a feeling of confidence.
The deploy question changes accordingly. It stops being “do I believe the agent?” and becomes “did I see the evidence?” Evidence also survives handoffs: a teammate, or a future session with no memory of this one, can look at the same screenshots and reach the same conclusion.
Where it breaks down
Selectors are the flaky part. A directed session tolerates drift better than a committed script, because the agent re-finds elements by role and visible text instead of dying on a stale CSS path, but it still misfires: markup changes, and “found a submit button” is not always “found the right submit button.” I treat it as a property of the whole category of tooling, and I watch the screenshots rather than only the readbacks.
Auth walls are a harder stop, and they should be. Captchas and two-factor prompts exist to require a human; when my agent hits one, it stops and asks me, and that is correct behavior rather than a gap to engineer around. The tooling also gates itself. In one link-verification session I found the browser layer refusing navigation to most of the domains I cared about until each was granted permission. Budget for a human in the loop, because some of these walls are designed to find one.
Maintenance is the honest cost. A committed end-to-end suite is a product: fixtures rot, selectors drift, and someone owns the rot. Recurrence is the variable that decides. Recurring checks earn committed automation, like my Lighthouse gate running the same four pages every time. Per-change verification fits directed sessions, where the agent’s flexibility replaces a script’s brittleness. The suites I maintain today stay at the unit and integration level.
And none of this judges quality. The browser can prove the page renders and the form submits. Whether the page is any good remains my call.
Ask it to open the page
The operating rule I’ve settled on: an agent’s claim about its own UI work is a draft until something independent has watched the behavior. The agent has hands now, and eyes. When it tells you the fix works, don’t ask it to explain the code again. Ask it to open the page.