A coding agent can run cypress run and read the exit code. That tells it whether a spec passed. It does not show the Command Log, the DOM at the failed command, the accessibility tree, or the browser state a human sees in Cypress open mode.
This gap matters when the failure is visual or stateful. “Element not found” could mean the selector is wrong, the page never loaded, an overlay covered the control, or the test inspected the wrong retry attempt.
cypress tap adds a terminal interface to a live Cypress open-mode session. The interesting part is not that an AI can execute another command. It is that the agent can inspect the same evidence a developer would use before proposing a fix.
Start Cypress and select a Chromium-based browser:
npx cypress open --e2e --browser=chrome
In another terminal:
npx cypress tap specs --json
npx cypress tap run cypress/e2e/login.cy.ts --json
npx cypress tap status --json
The run command requests a run and returns immediately. It does not mean the spec has started or finished. A reliable agent must poll status, set its own timeout, and wait for a terminal passed or failed state.
There is a subtle stale-state trap: the previous run's verdict remains readable until the next run starts. The JSON status includes startedAt; compare it with the run you requested before trusting the verdict.
That produces a bounded protocol:
type TapStatus = {
status:
| "not connected"
| "browser not selected"
| "spec not selected"
| ""
| "running"
| "passed"
| "failed";
startedAt: string | null;
};
async function waitForVerdict(requestedAfter: number) {
for (let attempt = 0; attempt < 60; attempt++) {
const status = await runJson<TapStatus>("npx cypress tap status --json");
const started = status.startedAt ? Date.parse(status.startedAt) : 0;
if (started >= requestedAfter &&
(status.status === "passed" || status.status === "failed")) {
return status;
}
await delay(500);
}
throw new Error("Cypress session did not produce a fresh verdict");
}
The timeout is part of the correctness model. A build failure can leave the session in ``, so “poll until success” can otherwise become an agent loop.
There is another important contract: cypress tap status exits successfully for a determinable stage, including a reported test failure. Automation must branch on the JSON status, not treat shell exit code zero as a passing spec. Prefer --json, pin Cypress in the lockfile, and let the agent inspect cypress tap --help so its command assumptions match the attached session.
Once the run fails, get the reporter view:
npx cypress tap reporter --json
npx cypress tap reporter --test-id r3 --attempt 1 --json
The test-level report includes routes, hooks, the Command Log, and failure details. If retries occurred, inspect the failed attempt instead of assuming the final attempt represents the original failure.
Then narrow the investigation:
npx cypress tap command \
--test-id r3 \
--command-id 7 \
--attempt 1 \
--json
npx cypress tap pin \
--test-id r3 \
--command-id 7 \
--attempt 1 \
--at after
npx cypress tap aria --selector "main"
npx cypress tap inspect --selector "[data-testid=submit]"
pin restores a command snapshot into the app-under-test frame. dom, aria, and inspect can then read that historical state. This is far stronger evidence than asking an agent to infer browser state from an error string.
Browser access should not become an unbounded “fix until green” loop. I use a small policy:
The agent should also report what it observed and why the proposed edit addresses that evidence. A green rerun is necessary, but it does not prove the change preserved the intended assertion.
cypress tap is beta and requires Cypress 15.21.0 or later. It works with cypress open, not headless cypress run, and currently requires a Chromium-based browser. Its commands and output may still change between releases.
Beta also changes the evidence contract. Store the Cypress version, browser, testing type, session identifier, spec path, attempt, and startedAt beside any captured report. A JSON field that changes in a later release should fail your adapter clearly instead of being interpreted as a new test result.
It also exposes page structure to the terminal. Treat DOM text, routes, and console properties as potentially sensitive, especially when a coding agent sends context to a remote model.
The useful mental model is simple: the agent is not replacing the test runner or the reviewer. It is gaining a structured window into the live evidence between “failed” and “I know why.”