Staged repairsbrowse guides

Staged repairs & the drift gardener

The loop that fixes broken selectors for you: the green gate, what it may never rewrite, and the one switch deciding whether it applies or only proposes.

Most test maintenance is not interesting. A button gained a wrapper, an id moved, a form grew a field, and four tests now fail on a selector rather than on anything about your product. The drift gardener is the loop that does that work.

Read the next sentence before turning anything on.

The gardener is an actor, not a suggestion engine. In its shipped mode it rewrites the steps of your saved tests, by itself, on a schedule, with nobody in the room. The mode where it only proposes is the other one, and which of the two a project gets is a single checkbox.

It is a Pro plan feature (auto-repair), it needs the workspace's own AI key, and it never runs on a project that has not opted in.

The two modes, and the one switch

Each project chooses exactly one. The switch is Project settings → General → Autonomous repair, "Repair failed runs automatically".

Auto-repairWhat triggers itWhat it does
OnA failed unattended run — a scheduled suite, or a run finished by the workerA proven fix is written to the test immediately. Nothing is staged for review.
OffA radar sweep finding an app change that touches a selector a test usesThe fix is proven the same way and staged in a queue. Nothing is written until you approve it.

Note what triggers each. The applying gardener is failure-driven: nothing happens until a run goes red. The proposing one is change-driven: it works from what the radar noticed, which is why it needs the radar switched on to do anything at all.

This is one gardener per project, enforced where the work is claimed rather than where it is requested: the staging pass skips a project that has auto-repair on, and re-checks the flag per test. A test is never handled by both.

The consequence is stated on the queue itself: on an auto-repair project, an empty "Staged repairs" list means handled elsewhere, never nothing is broken.

The ladder, and the green gate

Both modes climb the same three rungs, stopping at the first that works:

  1. Self-heal — snapshot the live DOM, ask the model for a corrected selector for the step that failed.
  2. Suggest-fix — ask for a set of step operations (insert / replace / delete) against the whole test.
  3. Diagnose-drift — diagnose the failure first, and only then repair; this rung is also where a failure is classified as an app bug or an environment problem rather than test drift, in which case nothing is repaired at all.

Every candidate is applied to an in-memory copy of the test and re-run. The entire safety story is that re-run: a fix is never persisted unless the test goes green with it applied. Nothing is written on a model's confidence.

If the ladder exhausts without a green run, no fix is written and an AI triage issue is filed instead.

What the gardener may not do

The green gate has a floor beneath it, and it exists because the gate alone can be gamed. Rewriting expect /dashboard to expect /login re-runs green precisely because it no longer asserts anything — the gate would certify the very regression that broke the test.

So any operation that deletes or rewrites an outcome assertion is refused before it reaches the gate, on both rungs, at any confidence. The rest of the proposal continues without it, and the refusals are shown with their reasons:

Refused: rewriting the expectation at step 4 from "url contains /dashboard" to "url contains /login". Outcome assertions are only changed by a human — an auto-rewrite to observed reality cannot fail.

Repair rewrites the locator, never the expectation. If every proposed change was refused, the modal says so rather than reporting "no fix found".

The queue (auto-repair off)

Proposals live under Staged repairs on the project's App radar page, because that is where their cause is. Each row carries:

  • the proposed step changes, as an insert/replace/delete diff you can expand;
  • what triggered it — the app changes that implicated this test, with their URLs;
  • the failing step and the error from the fresh run that proved the test broken;
  • how long it has been staged, and how many later sweeps restated it.

Two actions:

  • Approve and apply — writes the change to the test and records who approved it. Revertable afterwards from the test's repair history.
  • Dismiss — declines the fix. It is not proposed again unless the test or the app changes.

At most one open proposal per test. A test has one step list, so two open proposals could never both be applicable — approving either instantly staleifies the other. A further app change that implicates the same test is recorded on the open row instead of stacking a second one.

A proposal also goes stale on its own. Its operations index into the steps the diff was computed from; edit the test in the builder and approving is refused rather than splicing a diff into a list that no longer exists. Re-run the sweep to get a fresh one.

Statuses a row can end in: proposed, applied, dismissed, stale, superseded.

Two more things the staging pass does before it spends anything. It dry-runs the test first — no AI — and if the test still passes, nothing is proposed and no model call is made. And it only considers a test whose selectors the change touched; a test that merely visits the changed page is left alone, because repairing something nothing broke is the fastest way to spend a key on nothing.

Why the queue may be empty

The queue says which of these it is, and they are not interchangeable:

  • This project repairs itself — auto-repair is on. Fixes are applied without passing through here.
  • Staged repairs need an AI key — the radar and its at-risk list work without AI and are unaffected; writing a repair is the only part that needs a key.
  • Nothing has been swept yet — no baseline, so nobody has looked.
  • No repairs waiting — every at-risk test still passes. This is the good one.

A staging pass dry-runs at most 10 tests. When more are at risk, the rest are named in the pass summary and picked up by the next sweep, never dropped. That summary is written to the server log; it has no screen and no email. The staged count is also not in the daily digest today — the queue on the radar page is where you find out.

The dry-runs and the proving re-runs happen server-side, even on a project whose tests are configured to run on your local agent.

What an auto-applied repair leaves behind

When auto-repair is on and a fix lands, the test's steps change under you. What makes that reviewable rather than silent:

  • The test records its last repair: which run triggered it, which strategy won, the exact operations, the timestamp, and — the important one — the complete pre-repair step list.
  • The test is badged Auto-repaired in the builder and on the run, opening a review modal with the diff. It also appears in the test's AI history alongside the generations and interactions.
  • Revert restores the pre-repair steps exactly. The confirmation is honest about the consequence: the selector the repair fixed will fail again until it is repaired another way. Mark reviewed clears the flag and keeps the fix.
  • The outcome fans out to your notifications as an auto-repair event, so a repaired test is not a run you never hear about.

Three limits on that record, all worth knowing before you switch this on:

  • It is one slot per test, not a history. A second repair overwrites the first, and with it the only snapshot you could have reverted to.
  • It carries no author, because an auto-applied repair has none. A repair you approved from the staged queue does record who approved it.
  • There is no page listing what the gardener changed — no "repaired this week" filter on the Tests page, no report. You find an auto repair by opening the test or the run it came from.

It does not go back through review

Say this out loud, because the two features look adjacent and are not. Human review of AI-written tests holds a newly generated test out of service until somebody approves it. A repair edits a test that is already in service, and applying one does not move it back to pending. The next schedule, suite and CI gate runs the rewritten steps.

The two gardeners differ here, and it is not cosmetic:

  • The proposing one asks the run-selection gate before it touches anything, so a test still awaiting its first approval is never dry-run or staged. The radar's at-risk list does the same.
  • The applying one does not. It reaches a test through a failed run, and a pending test can have a failed run — the generator's own first run, or a reviewer re-running it. On a project with auto-repair on, a machine-written test nobody has approved can therefore be machine-repaired, and it stays pending afterwards.

If you want a person between every machine edit and your suite, the setting for that is auto-repair off plus the staged queue. The review gate answers a different question.

Two things the gardener will not touch

  • A negative control run. A sensitivity check runs a test against a deliberately falsified expectation; it is required to fail. Handing that to a repair loop would spend a model call teaching a sentinel to pass, and a "repair" that succeeds there has corrupted the test. Control runs are skipped outright.
  • A scenario probe. Only saved tests are tended. A throwaway scenario run has nothing to keep green.

Doing it by hand

Nothing above removes the manual path, and it works whatever the project setting says. On a failed run you can ask for a Suggested fix, Diagnose the failure, or run the whole ladder once interactively — same rungs, same green gate, same refusals, with you watching it stream. Those are Pro-gated on the same auto-repair feature, and they spend your AI key like everything else here.

A control run is refused by every one of them, for the reason above.

From an MCP client, suggest_fix proposes a repair for a failed run and apply_repair applies one. Approving or dismissing a staged proposal is a dashboard and API action; there is no MCP tool for the queue.

See also