Put Browser Verification at the End of the Agent Loop

James Phoenix
James Phoenix

I use the browser to verify a stable implementation, not to discover whether every intermediate edit compiles.

The Browser Is a Powerful but Expensive Feedback Loop

Browser automation is one of the most capable tools I can give a coding agent. It can see the page, follow a real route, interact with controls, inspect responsive behaviour, and expose failures that types and unit tests cannot reach.

For this final browser pass, I use Playwright CLI. It gives the agent a real browser for navigation, interaction, inspection, screenshots, and acceptance evidence without making browser work the centre of every edit cycle.

It is also slow. Starting servers, establishing state, authenticating, navigating, and interpreting evidence can take minutes before a simple type error rejects the implementation.

The browser is an acceptance instrument, not the default inner-loop debugger. I get faster and more reliable work when cheap deterministic checks stabilise the implementation first and browser verification judges the completed flow afterward.

I Separate the Inner Loop From the Acceptance Loop

My inner loop uses the fastest check capable of rejecting the current mistake:

  1. Static types and generated contracts
  2. Lint and repository invariants
  3. Focused unit tests
  4. Integration and API tests
  5. Build and packaging checks
  6. Browser acceptance verification

This is an ordering rule, not a claim that lower checks are more important. Browser evidence can be the only evidence that matters for a visual or interaction claim. I still delay paying for it until the implementation has survived the cheaper gates.

The result is a nested loop:

edit -> typecheck -> focused tests -> integrate
                                      |
                                      v
                         browser acceptance pass
                                      |
                              fix real UI gaps
                                      |
                         final browser evidence

The fastest loop handles mechanical correctness. The browser handles experiential correctness.

What Goes Wrong When I Open the Browser Too Early

Early browser use repeatedly verifies code that is still changing structurally. Harness failures such as stale servers, expired sessions, missing fixtures, or port collisions can also look like product regressions.

Visual feedback then pulls attention toward spacing and copy while persistence or error handling remains incomplete. Manual clicking only proves that one run worked, while a focused test or script turns the discovery into reusable evidence.

Browser Tests and Agentic Browser Inspection Are Different

I do not postpone every test that happens to launch Chromium. A fast, deterministic Playwright test can belong in the ordinary test suite. If it starts from fixtures, executes without model-directed clicking, and finishes quickly, it behaves like any other automated integration test.

What I delay is the expensive agentic loop: opening a signed-in browser, exploring visible state, deciding where to click, taking screenshots, and reasoning about layout or interaction quality.

This distinction reconciles two useful practices:

  • Convert repeated browser flows into Playwright scripts so they become fast and reusable.
  • Save open-ended browser inspection for the acceptance stage, after code changes are complete.

The more deterministic the browser check becomes, the earlier I can run it. The more exploratory it remains, the later it belongs.

Why I Use Playwright CLI for Final Verification

Playwright CLI makes acceptance explicit and reproducible. I point it at the finished environment, follow the important user journey, inspect rendered state, and retain screenshots or traces. I use cheaper checks while iterating, then reach for Playwright CLI when the remaining uncertainty is browser-specific.

It verifies what the user can see and do. When a claim crosses system boundaries, I pair it with API, database, workflow, or telemetry read-back.

Warning: Multiple browser sessions can occasionally require cleanup. Parallel agents or repeated Playwright CLI runs may leave orphaned browser processes, stale profiles, duplicate development servers, occupied ports, or conflicting authenticated state. Before treating the symptom as a product defect, I close known stale sessions, stop task-specific duplicate servers, and reset the relevant test state. I keep cleanup targeted so I do not disrupt unrelated browser work.

When I Use the Browser at the Start

There are legitimate exceptions. If the defect only exists in the browser, I reproduce it once before editing. Examples include an overlay hiding a switcher, a mobile action falling below the fold, a focus trap, a hydration race, or an animation that never settles.

That first pass establishes the failure and captures a useful baseline. I then leave the browser, work through faster checks, and return for final verification.

For visual implementation against a reference, screenshots may also be the specification. In that case browser capture belongs throughout the convergence loop, but I still mechanise it. A script produces fixed-view screenshots, a deterministic image comparison calculates the loss, and the agent works against stored artifacts instead of repeatedly improvising navigation.

Use the browser early to establish a browser-only fact, not as a substitute for every cheaper diagnostic.

The Final Browser Pass Must Test the Claim

A generic “page loads” check is weak evidence. The final pass should match the product claim I intend to make.

Leanpub Book

Read The Meta-Engineer

A practical book on building autonomous AI systems with Claude Code, context engineering, verification loops, and production harnesses.

Continuously updated
Claude Code + agentic systems
View Book

If I changed onboarding, I test the complete onboarding transition and resulting session state. If I standardised layouts, I inspect every registered route at the relevant viewports, including loading and error states. If I fixed persistence, I combine browser interaction with an API or database read-back rather than trusting the visible success message.

I capture:

  • The exact environment and deployed version
  • The route, viewport, identity, and fixture used
  • Initial and resulting states
  • Screenshots or traces where they clarify behaviour
  • Any claims that remain outside the pass

This keeps browser evidence honest. A screenshot proves what was visible. It does not prove which database row was written, which workflow ran, or whether a retry is idempotent.

Turn Discoveries Into Cheaper Gates

The browser often reveals a missing invariant. Once I understand the failure, I move as much prevention as possible into a cheaper layer.

A hidden account switcher can become a stacking-context component test. Inconsistent route widths can become a route registry lint. A stale authenticated redirect can become an integration test. A recurring visual mismatch can become a screenshot diff.

This is how the loop compounds. The expensive browser pass discovers a class of defect once. A deterministic gate prevents the class on every future change.

The Rule

I ask one question before opening the browser: what uncertainty can only the browser resolve right now?

If types, tests, API calls, logs, or stored artifacts can answer it faster, I use those first. If the uncertainty is genuinely visual, interactive, responsive, or environment-specific, I use the browser and capture evidence proportionate to the claim.

Putting browser verification last does not make it optional. It makes it the final judge of work that is ready to be judged.

Related

Topics
Agent ReliabilityCoding AgentsDebuggingTestingVerification

Newsletter

Become a better AI engineer

Weekly deep dives on production AI systems, context engineering, and the patterns that compound. No fluff, no tutorials. Just what works.

Join 306K+ developers. No spam. Unsubscribe anytime.


More Insights

Cover Image for Turn UI Consistency Into a Lint Rule

Turn UI Consistency Into a Lint Rule

If every page should use the same shell, loading pattern, and width policy, consistency should be checked instead of remembered.

James Phoenix
James Phoenix
Cover Image for Treat External SaaS Configuration as a Checked Registry

Treat External SaaS Configuration as a Checked Registry

If application behaviour depends on values in a third-party dashboard, those values belong in a machine-checked inventory.

James Phoenix
James Phoenix