Build1 publisher3 min readPublished
One hand-translated Cypress spec produced the 14 rules that governed 89 agent migrations
A dev.to writeup puts a 90-spec Cypress-to-Playwright migration at four working days with Claude Code. Most of the design work was 90 minutes of hand translation and one rule telling the agent when to stop.
The Engineer · Build desk

What happened
- A dev.to author reports moving a 90-spec Cypress suite to Playwright in four working days with Claude Code, against team estimates that all landed at two sprints or three.
- He translated one checkout spec by hand in about 90 minutes, with no AI, and turned each decision it surfaced into a file called MIGRATION_RULES.md that reached 14 rules.
- Verification ran at two levels: three runs under --repeat-each=3 before the agent could report success, and ten consecutive passes before a Cypress spec was deleted.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- capability A STOP condition on unresolvable helpers converts an unreadable 600-line commands.js into a queue of named human decisions, which is what lets one reviewer supervise 89 translations.
- constraint Parity is judged against the old suite, so a team whose Cypress run flakes cannot trust its screenshot baseline until it stabilises the reference first.
- cost The spend moves from engineer hours to CI minutes: the ten-pass deletion gate alone is ten full suite runs, and someone pays for that compute.
- decision Batch size stops being a matter of taste and becomes a number you set below the point where your agent starts inventing helper names.
The 90 minutes of hand work bought a list of decisions a converter would otherwise have made silently. `cy.get('[data-cy=x]')` became `page.getByTestId('x')`, which meant changing the test-id attribute in `playwright.config.ts` [10]. Cypress's implicit retry-until-assert became an explicit `await expect(locator).toHaveText()` [25]. The `cy.login()` custom command, which hits an API and sets a cookie, became a `storageState` fixture created once per worker, not per test [11]. The author also reports getting two `page.route()` matchers wrong on the first try, because the glob semantics differ from Cypress's minimatch [12].
In my view the line that makes the rest safe is rule 1: "Never translate a custom command inline. Look it up in cypress/support/commands.js, then map it to the fixture in tests/fixtures/*.ts. If no fixture exists, STOP and report which command is missing." [15] The suite it was aimed at had grown a 600-line `commands.js` of helpers that nobody fully understood anymore [5]. A STOP condition turns each unresolvable helper into a human decision that arrives by name, one at a time, instead of a plausible guess that passes.
Batch size came out of observed failure. Past roughly 8 to 10 specs in a single session, the author says, the agent started "remembering" helper names that didn't exist, and fresh sessions fixed it [18]. So the work ran five specs per session, with a prompt forbidding any file outside `tests/e2e/` [17]. Eighty-nine specs in fives is 18 sessions [3], each reloading the rulebook and one worked before/after example from the project spec file Claude Code reads on startup [14]. "Five diffs is a coffee break. Eighty-nine is a weekend," the author wrote [19].
The gate is where the machine time goes. Rule 5 makes the agent run each spec three times with `--repeat-each=3` and report success only if all three pass [16]. No spec was deleted from Cypress until its Playwright twin passed 10 times in a row [8]. The old suite ran in about 38 minutes on CI [4]; ten passes at that rate is 380 minutes, a bit over six hours of execution [4], and the reason to expect less is the first-class parallelism that motivated the move [7].
Behaviour parity is judged against the old suite: the screenshot diff compares the Playwright run to the Cypress run, and drift sends the spec back for a rule or fixture fix [21]. That Cypress suite flaked roughly once every four runs [4]. A reference implementation failing a quarter of its runs will hand back diffs that are flake rather than drift, and separating the two is manual work. The writeup does not say how many specs the gate rejected [26].
Four days is one engineer's account of one suite. Against his own measured hand rate the agent bought a lot: 90 specs at 90 minutes each is 8,100 minutes, or 135 hours, about seventeen eight-hour days [1]. Against the team's "two sprints, maybe three" [6] the multiple depends on a sprint length the writeup never defines; at two weeks per sprint that is 20 to 30 working days, five to seven and a half times four [2]. For the figure to transfer you need helpers enumerable in one file, test ids uniform enough that a single config change covers them [10], and a green-enough Cypress run to diff against. One of the four days went to two traps, by the author's own accounting [24][5].
What to watch
- Publication of MIGRATION_RULES.md and the count of specs the screenshot gate rejected would let someone else check the four-day figure.
- The second trap: the writeup promises two, and the published text stops inside the first.
- Whether the same STOP rule holds on a suite whose custom helpers live across several files instead of one commands.js.