# Healing

When a selector stops matching, Squiggle looks for the same control, uses it if it's confident, and writes the new selector into your flow file. You review the change in your next diff.

## What healing does

Step through a redesign of a sign-in screen, and compare a test that taps a fixed point, a selector that never heals, and Squiggle:

When a selector matches nothing, Squiggle scores the controls that could be the same one, by name, testID, role, label, text and the line of source that renders it. If the best one scores high enough, the step uses it and passes, and the run reports what changed:

```shell
squiggle flow checkout
```

Output:

```text
healed testID: buy-button -> checkout-button (score 82, margin 22)
rewrote 1 healed selector(s)  .squiggle/flows/checkout.yaml  review the diff — a heal is a guess that worked once
```

If no control scores high enough (60 or more), the step fails as a control that wasn’t found, and the flow file is left alone.

### How close the call was

The margin is how many points the heal won by: its score minus the higher of 60 and the best score of any other control. A control and the components inside it count as one control. A heal with a margin under 20 is `thin`: a few points of difference between runs could send the step to a different control, so the run warns you:

```shell
squiggle flow drafts
```

Output:

```text
healed testID: save -> save-draft (score 74, margin 9, thin)
! 1 heal(s) with a thin margin  step 3 'save-draft' by 9  under 20 points from a different element — pin the selector to a testID
```

Accept the heal, or better, give the control a `testID`, so the next run doesn’t have to guess. A negative margin means the heal scored under 60 and was taken only because the flow uses `lossy-fallback`, marked `lossy`. Studio shows the margin beside each healed step, and a red line under a thin one. With `--json`, each entry in `heals` carries its `margin`.

The new selector is written into the flow file, so the next run doesn’t need to heal. Review the change before you commit it: a heal is a good guess, not a proof.

To see how the margin moves, take the tour below. Its flow taps a sort button whose `testID` has since been renamed, and the demo app lets you rename the button’s label while you watch. Delete a word and the margin shrinks. Change the whole label and the score drops under 60, so the step would fail instead of guessing. Under the margin is every term of the score, so you can see what it was built from.

## Choose a mode

Set `healing` at the top of the flow:

```yaml
flow: checkout
healing: strict
steps: …
```

| Mode | What it does |
| --- | --- |
| `adaptive` | The default. Heals when it is confident, and updates the flow file. |
| `strict` | Never heals and never changes the file. A moved control fails the step. Use it when the move itself should fail the test. |
| `lossy-fallback` | Also takes the best match when it isn’t confident, and marks it. Useful while a screen is changing every day; noisy after that. |

## Reading a run that healed

A run that passed with heals isn’t the same as one that passed cleanly. The report lists every heal, and Studio marks healed steps in the run. A flow run from standard input has no file to update, so the run tells you the heal wasn’t saved.
