e2e: seven replays, no model calls, and the 25 seconds a renamed button costs
Published
One agent step against our own /search page: seven replays made zero model calls and ran 4.9x faster than the recording run. Renaming the submit button handed the step back to the model.
tester-army/e2e drives a browser with a model, then tries to stop paying for it. Its README makes one claim a reader can check. An agent step that a later assertion verifies records its actions, and the next run replays them with no model calls until the app changes.
We wrote one case against our own site: open /search, type playwright, submit the form, then assert a result link is visible. Four arms on one machine, with model calls counted from the runner’s own report events, not from a provider bill.
1.Result
| One case, four arms | Runs | Model calls | Tokens | Test (median) | Wall (median) | Cache |
|---|---|---|---|---|---|---|
| Plain locators, no agent step | 5 | 0 | 0 | 1.79 s | 2.75 s | not used |
| Agent step, first run | 1 | 3 | 28,627 | 11.85 s | 13.17 s | missed, recorded |
| Agent step, replayed | 7 | 0 | 0 | 2.43 s | 3.29 s | replayed |
| Agent step, live (--no-cache) | 3 | 2–3 each | 79,460 | 10.70 s | 11.73 s | bypassed |
Tokens to record one agent step
28,627
One run with 3 model calls; the seven replays after it spent nothing
Median replay of that step
2.43 s
Against 11.85 s for the run that recorded it, the 4.9x in the title
The same flow as plain locators
1.79 s
What the route and anchor checks cost on top: a replay is 1.35x this
One renamed button, one direction
24.64 s
The recording missed the control, the model took the step over and re-recorded it
Seven replays in a row made no model calls, spent no tokens, and never rewrote the recording. The median replay took 2.43 s against 11.85 s for the run that recorded it. It is also 1.35x the same flow written as plain locators, and that gap is what the route and anchor checks cost.
2.The bill for renaming one button
Then we changed the app: the submit button’s label went from Search to Find repos. The recording says tap button Search, so the next run replayed the first action and could not find the control. It handed the step over, and the agent found the new name and finished the test.
| Change | Cache outcome | Model calls | Test time | Runs |
|---|---|---|---|---|
| Button label Search to Find repos | handed off, target-not-found | 2 | 24.64 s | 1 |
| Reverted the label back to Search | handed off, target-not-found | 2 | 28.52 s | 1 |
| Replayed after re-recording | replayed | 0 | 4.00 s / 2.43 s | 2 |
An entry remembers the control it recorded, not the intent behind it, so changing the label back is another miss. Both directions cost 2 model calls and about 25 s. The next passing run re-records, and the run after that replays again. A recording of tap button Search is stale the moment that name changes.
3.With no model key at all
The README’s last sentence is the cheapest one to check: tests without agent steps need no model. We removed the key from the environment and ran the same files again.
| Run | Model key | Result | Model calls | Wall |
|---|---|---|---|---|
| Plain locators | none | passed | 0 | 3.28 s |
| Agent step, replay | none | passed | 0 | 3.29 s |
| Agent step, live | none | failed, MODEL_PROVIDER_FAILED | 0 | 2.71 s |
| Agent step, replay after that | none | passed | 0 | 5.39 s |
A recorded step replays with no credentials at all: the entry on disk is all it needs. Only a live step reaches for a model, and that failure left the entry untouched. The replay after it still replayed, with no key.
Two problems on the way are worth recording. The OpenCode key stored in the CLI’s auth file answered 429 “Go usage limit exceeded” on both routes. Port 3000 replied HTTP 200 with a different app entirely (OWASP Juice Shop), and a status code alone would have measured the wrong site.
Decision
- Keep the cache on
- Seven replays cost 0 model calls and 0 tokens.
- Against 28,627 tokens for the one run that recorded the step
- Expect a miss on every UI rename
- One renamed button handed the step back twice, once on the change and once on the revert.
- At 24.64 s and 28.52 s
- Use --strict-cache in CI
- A stale recording is silent by default.
- The test passes and every run pays the model again until someone re-records it
- Budget the extra 0.63 s
- A replay is 1.35x a plain locator run.
- The route and anchor checks are not free. They are far cheaper than a model call
What we did not measure
- The n behind the interesting number: one recording run, one hand-off per direction. The 4.9x speed-up rests on a single recorded run.
- Their example models: ours was opencode.go/deepseek-v4.1-flash, not the gateway or Anthropic models the project documents, so turns per step are model-specific.
- A production build: the app under test was next dev on port 3100, so all four arms share the same dev-server overhead.
- Provider billing: model calls come from the runner’s own report events, not from the provider’s usage page.
- The data: /search reads our database, so the 96 matches and the card anchors the recording captured are not fixed inputs.
Raw rows, including the runs with no key and the failures above: docs/analysis/2026-10-07-tester-army-e2e-replay-without-model-calls.jsonl. This is our measurement of the claim, not a score for the tool.