Visual regressions are the quietest way a build fails you.
No exception. No red test. The CI pipeline is green. The button is still there. It just moved three pixels on the tablet breakpoint, or the font color degraded on Safari, or the modal now clips behind the nav on a 375-pixel viewport. Everything passed. You'll find out when someone screenshots it.
Everyone has shipped that kind of failure more than once. The problem isn't carelessness. The problem is that knowing a codebase too well is a form of blindness. You know where the button is supposed to be. Your eyes confirm what they expected to see. The regression lives in the gap between expectation and reality, and familiarity is great at bridging that gap invisibly.
That's what Cyclops is for.
It crawls your dev server and discovers routes reachable through internal links, so you do not have to maintain every entry by hand. Unlinked pages, authenticated states, and routes beyond its limits still need explicit coverage. Routes drift. You add a page, forget to update your screenshot manifest. The crawler doesn't care. It walks from the root, follows internal links, caps at fifty pages and three levels deep, writes the manifest, and hands it to the capture engine. One eye, no preconceptions.
Capture runs through Playwright. Every route, every screen, saved as a PNG. The before shots are your baseline, set once, or pulled from main. The afters are whatever you just built. The differ runs pixelmatch over the pairs at a 0.1 threshold, with anti-alias handling that affects which differing pixels are counted; it does not decide whether a change is a regression, and writes a highlighted diff image for anything that moved. Then the orchestrator branches, commits the changed screenshots, and opens a GitHub PR with before, after, and diff embedded directly in the PR body. Side-by-side tables in GitHub Markdown. No separate dashboard. The review surface is where the work already is.
That part is useful. The watcher is where it gets interesting.
You save a file. Hot-reload fires. After its two-second debounce, Cyclops starts capturing the configured routes in the current state and logs one line to the terminal:
📸 UI updated! New screenshots saved for AI review.
That's the whole handshake. The screenshots land in screenshots/current_state/. Claude Code, or any AI assistant with filesystem access, can see the actual rendered UI, not the code, not your description of the UI, and work from that.
This changes the iteration loop in a specific way. You're not explaining to the AI what the page looks like and hoping the model builds a useful mental image. You're showing it. The screenshots record the states that were actually captured. Staleness, missing routes, and failed capture still need checking. The AI can tell you the card grid broke at the mobile breakpoint because it can see the card grid breaking. Every save is a new observation. The feedback cycle stays grounded in what actually rendered, not what you said rendered.
Whether you use the AI loop or not, the regression audit stands on its own. Run it before a merge. Run it after a dependency bump. Run it after any change touching shared styles or layout components, the kind of change where the damage is usually somewhere you weren't looking.
The build lies to you. Not on purpose. Just because you know it too well to see it clearly anymore.
The tool did it too. The installed CommonJS call to require("pixelmatch") returned a module wrapper instead of the callable under the checked Node runtime. The differ now uses .default. The regression detector had a regression. A tool outside the actual review loop can break quietly for a long time.
The October 25 check passed all seven automated tests. A separate real-PNG control changed exactly 400 pixels and the differ counted 400. Git, release, and publishing paths in the suite use mocks; that result does not verify a live browser crawl, interactive onboarding, or a GitHub publication. The earlier claim of a complete end-to-end recheck was too broad. The engagement report records what ran and what did not.
The October 26 follow-through used real Chromium against an owned local server. Two pages captured, a deliberate 404 failed, then changing the fixture triggered the actual watcher and produced a different screenshot in current_state. The watcher reported the failed route alongside the successful captures. The first run found a missing Playwright browser installation; installing the matching headless Chromium resolved that setup failure. The recorded output binds the result to hashes of the capture, watcher and utility source. The replay checker copies those files into a temporary workspace and uses the checkout's installed dependencies:
curl --fail --output cyclops_browser_check.cjs \
https://ghostintheprompt.com/research/cyclops_browser_check.cjs
node cyclops_browser_check.cjs /path/to/cyclops
This checks one settled file change, not every edit during an active capture. The watcher currently drops triggers while it is capturing, and a failed route can leave an older file on disk. Review the current run's route outcomes before treating a screenshot as fresh. Interactive onboarding, route discovery and GitHub publication remain outside this check.
One eye on everything, no prior expectations. That's the point.
The handoff between Playwright capture and AI assistant is the same shape as the agent-to-agent problem in the six-vector agentic-AI attack surface, the model can't reason about pixels it didn't see, and the loop is only honest when the screenshot is fresh.
GhostInThePrompt.com // The regression lives in the gap between expectation and reality. Familiarity bridges that gap invisibly.