How a recording is checked
A recording is a small program the agent wrote. Saffron does not take the agent's word that it works: before a proposal is filed, it runs the recording in ways that should pass and in ways that should fail, reads it for things that break on a later day, and writes every surprise beside its step as a note. All of it is replay or reading, with zero AI calls. The detail of each check is in §5 and §7; this section is the map of the checks that decide what is filed.
In order
| When | What it catches | |
|---|---|---|
A Then cannot act |
While recording | A check that types, navigates or clicks a button that submits (Save, Submit, Log in) is refused before the browser runs it, so a check never makes itself true (§16). Any other click under a check is recorded and held (check changed the page:). |
| A selector that matches nothing | While recording | A check whose CSS selector finds nothing on the page is refused, at no cost (§5). |
| The proof replay | After recording | The recording replayed alone, in a fresh execution, with new {unique:...} values and no agent. One that fails gets one refinement pass, then is filed UNVERIFIED with the reason (§5). |
| What the proof saw | During the proof | A write to the app's own site cut off by the page moving on (write not finished:), a text check held by a whole card (broad check:, advice), and a capture that read other text than the agent typed (capture differs:, advice) (§5). |
| Stability proofs | After a passing proof, if turned on | The recording replayed again, each time in a fresh execution, to catch what held only in the recording's own session (unstable recording:). |
| Every Outline row | After a passing proof | The recording replayed on each other row it serves, with that row's values (row not proved:) (§5). |
| The sensitivity check | After a passing proof | Each Then run on the page as it was just before the step it follows: a check that passes there too proves nothing (insensitive check:), and a "not shown" check must see something disappear (§7). |
| What a heal kept | After a heal | A healed step that no longer enters, picks or uploads what it did (behavior change:), or no longer keeps what the committed recording required (requirement not kept:) (§7). |
| Choices | After recording, no browser | A choice opened through a control named with the row's value, which names it before the choice only on the row it was recorded on (choice opener:, advice). |
| Locators that move | After recording, no browser | A generated id, hash, month heading or price a later load will not find (volatile locator:) (§3). |
| A check that does not hold | While recording | Each browser check is run on the agent's page by replay's own engine before it is recorded, and refused with replay's reason when it does not hold (§5). |
| A check weakened | After recording, no browser | A Then recorded again under the same words with a looser check than the committed one (check weakened:) (§5). |
| A moved clock | After recording, no browser | Every {date} token resolved for tomorrow, month end and year end, and the text beside it must still match (date not following the clock:) (§5). |
| Data kept as written | After recording, no browser | A {data:...} value a step names that its recording holds as text (data not followed:) (§5). |
| Wording | After recording, no browser | A new step that recorded exactly the actions of an existing one, with the words they share (duplicate wording:, advice, with a feature edit to the existing wording). |
| A recording that cannot be right | Last | A Then that recorded no check, or a fill that typed nothing where the step passes a value: filed UNVERIFIED, even when the proof passed. |
What a note does
Every note names the step it is about, with its file and line, and says
what to change. Some only inform; the rest hold the proposal: accept --all skips it, and a person accepts it by its file after reading why.
| Note | Held from accept --all |
Accept it by file when |
|---|---|---|
behavior change: |
yes | the scenario still tests what it says |
requirement not kept: |
yes | it is no longer required |
insensitive check: |
yes | the check is meant to hold either way |
volatile locator: |
yes | that value never changes |
unstable recording: |
yes | the difference between executions is expected |
row not proved: |
yes | that row is meant to fail with this recording |
write not finished: |
yes | nothing reads what that write saves |
check changed the page: |
yes | the check needs nothing the step did beyond opening what it checks |
date not following the clock: |
yes | that date never moves |
data not followed: |
yes | the step means that value, whatever the data file says |
check weakened: |
yes | the looser check still says what the step says |
seeded from another recording: |
no, advice | |
broad check: |
no, advice | |
choice opener: |
no, advice | |
capture differs: |
no, advice | |
duplicate wording: |
no, advice |
accept --all also skips a proposal no proof replay checked, and one
that is UNVERIFIED.
What is on by default
| Default | Change it with | |
|---|---|---|
| The proof replay, and every replay after it | on | --no-verify or "verifyProposals": false turns off the proof and the stability, row and sensitivity replays, for a scenario whose side effects cannot run twice. The checks made while recording and those that read the recording still run. Its proposals are held as not checked. |
| Stability proofs | off (0) | "stabilityProofs": 2 in saffron.config.json, or --stability-proofs 2 for one run (0 to 10) |
| Every Outline row | every row | "rowProofs": 5 proves at most five other rows; the rest replay the proposal on their own row later in the run |
| The sensitivity check | on, with the proof | Not configurable. Its insensitive check: note holds the proposal, but never makes it UNVERIFIED. When its extra execution cannot run (the reset fails, or the steps before a check do not replay a second time), that check is left unprobed, never flagged. |
Each proof is another execution of the scenario: a reset command
(§17) runs before it, so a scenario that writes data starts from the same
state each time.
Turning on stability proofs
{ "stabilityProofs": 2 }
After a recording passes its proof, it is replayed twice more before it is filed. Each replay is a full run of the scenario, so recording takes longer; replays in CI are unaffected, since nothing is recorded there. It is worth it for flows that depend on a session, a timer or shared data: a recording that only held because of something left over from its own run fails one of these, and is held with the failure named.
Measuring the result
These checks decide what is filed. The benchmark measures what was filed: it replays the example suite's committed recordings on the demo app, then on deliberately broken versions of it, each a change that should turn one scenario red, and publishes how many stayed green.