saffron-ai 0.9.7 → 0.9.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +217 -0
- package/README.md +2 -3
- package/dist-pkg/cli.js +239 -215
- package/package.json +2 -2
- package/skills/saffron/SKILL.md +13 -1
- package/skills/saffron/references/config.md +6 -4
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,223 @@ the VS Code extension (`ChathurangaJayasinghe.saffron-vscode`) and the
|
|
|
5
5
|
JetBrains plugin (`ai.saffron.jetbrains`). Dates are publish dates. Each
|
|
6
6
|
release heading names its main feature: `## <version>: <title>`.
|
|
7
7
|
|
|
8
|
+
## 0.9.9: A browser matrix in one run
|
|
9
|
+
|
|
10
|
+
Released 2026-10-06.
|
|
11
|
+
|
|
12
|
+
Runner
|
|
13
|
+
|
|
14
|
+
- **A browser matrix in one run.** `saffron run --browser
|
|
15
|
+
chromium,firefox,webkit` (or a list in `browser`) runs the suite in each
|
|
16
|
+
browser, one after another, into one report: a row per scenario per
|
|
17
|
+
browser, in the terminal, the report, the history and the dashboard.
|
|
18
|
+
Chromium-family browsers go first, so Firefox and WebKit replay what
|
|
19
|
+
they recorded. Every browser starts before anything runs. Screenshots
|
|
20
|
+
and traces are kept per browser, and `saffron trace` names them
|
|
21
|
+
`feature:scenario@browser`. The dashboard stands a scenario at its
|
|
22
|
+
worst browser and never calls a difference between browsers flaky.
|
|
23
|
+
Refused with `--shard`. Every result now names its browser, so running
|
|
24
|
+
one browser again replaces that browser's rows and keeps the others'.
|
|
25
|
+
`saffron status --json` (and the MCP `project_status` tool) names the
|
|
26
|
+
browser of each row that needs attention.
|
|
27
|
+
- **Device and viewport profiles.** `--device "iPhone 15"` (config
|
|
28
|
+
`device`) runs every scenario under a Playwright device profile,
|
|
29
|
+
recordings and heals included; `--viewport 1280x720` (config `viewport`)
|
|
30
|
+
sets the size alone. An unknown device names the closest ones, and a
|
|
31
|
+
mobile profile in Firefox stops the run up front.
|
|
32
|
+
- **Parallel recording and healing.** `--agent-workers N` (config
|
|
33
|
+
`agentWorkers`, default 1) lets up to N agent sessions run at once,
|
|
34
|
+
each in a browser of its own. An outline's rows stay together on one
|
|
35
|
+
worker. Each session is paid for: raise it deliberately.
|
|
36
|
+
- **Shards know their browser.** A shard records the browser it ran in;
|
|
37
|
+
`saffron report --merge` refuses shards of different browsers, a
|
|
38
|
+
re-run attempt replaces only its own browser's shard, and each
|
|
39
|
+
browser's merge keeps its own history line.
|
|
40
|
+
- **A bad storage state stops the run first.** A `storageState` file that
|
|
41
|
+
is missing, unreadable or not JSON now stops the run at startup, exit 2,
|
|
42
|
+
before `--rerecord` deletes any recording.
|
|
43
|
+
- **No second replay without an agent.** With `--workers` and
|
|
44
|
+
`--no-agent`, or in Firefox and WebKit, a scenario that went red in the
|
|
45
|
+
parallel phase is no longer run again afterwards: nothing could change
|
|
46
|
+
its result.
|
|
47
|
+
- **Per-worker timing.** The report has a timing panel (each worker's
|
|
48
|
+
runs, busy time and share, and the five slowest scenarios), and the
|
|
49
|
+
terminal prints each worker's share and the three slowest scenarios.
|
|
50
|
+
Merged shards list each machine's workers.
|
|
51
|
+
- **Saffron's own data in the replay.** Each step shows the tabs it
|
|
52
|
+
opened or switched to and the response each network check matched,
|
|
53
|
+
and the Saffron pane shows what the agent cost for the scenario. The
|
|
54
|
+
report's steps carry `tabs` and `responses` for tools.
|
|
55
|
+
- **Browsers in the editors' contracts.** `saffron status --json` and
|
|
56
|
+
`saffron report --run <id> --json` give each row its `browser`, and the
|
|
57
|
+
run's `browsers` when its rows span several.
|
|
58
|
+
|
|
59
|
+
VS Code extension 0.6.2
|
|
60
|
+
|
|
61
|
+
- After a browser-matrix run, Last Run and a CI run's results label each
|
|
62
|
+
row with its browser, and Open Replay and Open Trace open that
|
|
63
|
+
browser's execution alone (`feature:scenario@browser`).
|
|
64
|
+
|
|
65
|
+
JetBrains plugin 0.6.2
|
|
66
|
+
|
|
67
|
+
- The same in the Last Run and CI Runs tabs: rows say their browser, and
|
|
68
|
+
the replay opens that browser's execution alone.
|
|
69
|
+
|
|
70
|
+
## 0.9.8: Run one scenario, from the editor too
|
|
71
|
+
|
|
72
|
+
Released 2026-10-06.
|
|
73
|
+
|
|
74
|
+
Behaviour changes
|
|
75
|
+
|
|
76
|
+
- **Tags now apply as in Cucumber**: a Feature's and a Rule's tags to
|
|
77
|
+
every scenario in it, an Examples block's to its rows. A CI filter can
|
|
78
|
+
select differently than before: `not @ignore` with `@ignore` on a
|
|
79
|
+
Feature now skips that feature, and `--filter @smoke` with a
|
|
80
|
+
Feature-level `@smoke` now takes all of its scenarios. Check your
|
|
81
|
+
`--filter` expressions when you upgrade. The dashboard's results per tag
|
|
82
|
+
and the IDEs' Tags tabs count the inherited tags too.
|
|
83
|
+
|
|
84
|
+
Runner
|
|
85
|
+
|
|
86
|
+
- **Run one scenario by its line or its name.** `saffron run
|
|
87
|
+
features/login.saffron:12` runs the scenario that line is in,
|
|
88
|
+
Cucumber-style: its Scenario line, a tag above it or any line inside it.
|
|
89
|
+
An Examples row runs that row alone, any other line of an outline runs
|
|
90
|
+
every row, and `:12:30` names two. `features/login.saffron:Successful
|
|
91
|
+
login` names it instead, which stays true while the file is edited,
|
|
92
|
+
and `:Roles (example 2)` one Examples row, as `saffron trace` lists it. A
|
|
93
|
+
line no scenario owns (the feature's header, a Background, a StepSet, a
|
|
94
|
+
Rule's header, past the end), a name no scenario has, or an outline
|
|
95
|
+
whose Examples have no rows yet stops the run with exit 2 and says
|
|
96
|
+
what is there. Works with `--filter`, `--shard` and `--rerecord`.
|
|
97
|
+
- **Tags on a Feature, a Rule or an Examples block apply to what they
|
|
98
|
+
hold**, as in Cucumber: `--filter @smoke` takes a feature tagged
|
|
99
|
+
`@smoke`, and `not @wip` leaves out a Rule's scenarios, or an Examples
|
|
100
|
+
block's rows, tagged `@wip`. Reports, history and the IDEs' tag lists
|
|
101
|
+
carry them; a scenario lists every row's tags.
|
|
102
|
+
- **A scenario whose own name ends in "(example N)" keeps its results.**
|
|
103
|
+
Report and history rows now carry the scenario's name; an older row is
|
|
104
|
+
read as a scenario of that exact name first, and as an outline's row
|
|
105
|
+
only when no such scenario exists, in status, the dashboard and CI
|
|
106
|
+
results alike.
|
|
107
|
+
- **A run that writes an outline's recording runs every row of it**
|
|
108
|
+
(`--rerecord`, or an outline not recorded yet or whose recording is
|
|
109
|
+
stale), also rows an Examples line or a `--filter` on Examples tags left
|
|
110
|
+
out: the rows share one recording. A row asked for records first and
|
|
111
|
+
the others replay the new recording in the same run, so one that fits
|
|
112
|
+
only that row comes back red, not as a proposal.
|
|
113
|
+
- **The dashboard keeps each Examples row's latest result** from whichever
|
|
114
|
+
run ran it: a run that left a row out (a tag filter, a named run) no
|
|
115
|
+
longer clears that row's failure; a row removed from the table stops
|
|
116
|
+
counting.
|
|
117
|
+
- **A run of named scenarios is not a suite run.** Its results replace
|
|
118
|
+
those scenarios' own in the last report, beside the rest of the last
|
|
119
|
+
run, and the report takes the run's own times and hook results. Its history line is
|
|
120
|
+
marked partial: the dashboard counts it for those scenarios (their
|
|
121
|
+
latest result, never run, recent failures), while trends, the pass-rate
|
|
122
|
+
series, the last run's figures and the next run's comparison count runs
|
|
123
|
+
of the suite only. A last report that was imported from CI, for another
|
|
124
|
+
base URL or billed another way (subscription or API key) is replaced,
|
|
125
|
+
and the run says so. Sharded, such a run merges the same way.
|
|
126
|
+
- **Two scenarios of one name in one file stop the run**, as any two
|
|
127
|
+
scenarios sharing a recording do: one per Rule was allowed, and each
|
|
128
|
+
overwrote the other's recording. The check now reads the configured
|
|
129
|
+
`features` folder, not only `features/`, and always the files the run
|
|
130
|
+
names.
|
|
131
|
+
- **One pass rate everywhere.** The run report, the trends and `saffron
|
|
132
|
+
status --json` (a new `passRate` per run) leave out a heal whose proof
|
|
133
|
+
replay failed, as the dashboard already did: the same run no longer
|
|
134
|
+
reads 100% in the report and 0% on the dashboard.
|
|
135
|
+
- **History written on Windows counts on the dashboard.** A history line
|
|
136
|
+
naming `features\login.saffron` is the same feature as
|
|
137
|
+
`features/login.saffron`, so its scenarios keep their latest result and
|
|
138
|
+
recent failures instead of reading as never run.
|
|
139
|
+
- **A refused run deletes no recording.** `--rerecord` removes recordings
|
|
140
|
+
only when the scenarios are about to run: a run stopped first (a cache
|
|
141
|
+
collision, an `{env:...}` or `{data:...}` reference, an invalid
|
|
142
|
+
`--trace`, `--browser` or `--assertion-policy`, a failed setup hook, a
|
|
143
|
+
missing browser) keeps them.
|
|
144
|
+
- **A path that does not exist stops the run.** `saffron run
|
|
145
|
+
features/chekout.saffron` (a typo) exits 2 naming it, instead of
|
|
146
|
+
running the other paths alone.
|
|
147
|
+
- **Run lenses from the language server**, for an editor that asks for
|
|
148
|
+
them (`initializationOptions.runLenses`). `saffron lsp` puts Run, Replay
|
|
149
|
+
only and Headed above each scenario, in the file's own Gherkin
|
|
150
|
+
language, each carrying the scenario's `file:name` and the project to
|
|
151
|
+
run it in. A `.feature` file outside a Saffron project gets none, and
|
|
152
|
+
neither does a file outside the editor's workspace, so a lens never runs
|
|
153
|
+
anywhere the user did not open (such a file keeps its own project's
|
|
154
|
+
completion and diagnostics). A scenario named like lines (`42`,
|
|
155
|
+
`10:30`) is targeted by its line. The JetBrains plugin also refuses a
|
|
156
|
+
lens whose folder is outside the IDE project.
|
|
157
|
+
- `saffron status --json` gives each scenario's line from the parsed file:
|
|
158
|
+
right in another Gherkin language, and never a `Scenario:` line inside a
|
|
159
|
+
doc string.
|
|
160
|
+
- **Open Trace on a CI run works from the editors again.** `saffron trace
|
|
161
|
+
--run <id>` takes `--max-download <MB>`, as the JetBrains plugin passes
|
|
162
|
+
it, and refuses a larger download exactly as `report --run` does: exit
|
|
163
|
+
2, and with `--json` the same `kind: "too-large"` and `sizeBytes`.
|
|
164
|
+
- **Two editors can load one CI run at once.** Each request downloads
|
|
165
|
+
into its own temporary folder beside `.saffron/ci/<provider>-<id>/`, so
|
|
166
|
+
one never deletes another's download. When another request finished
|
|
167
|
+
first, Saffron uses its folder, checked as a cached one is, and removes
|
|
168
|
+
its own. A finished download removes only earlier attempts of the run,
|
|
169
|
+
never a later attempt or another request's download in progress; what
|
|
170
|
+
a killed download left (0.9.7's `.partial`, or a folder untouched for a
|
|
171
|
+
day) goes.
|
|
172
|
+
- **`--max-download` covers everything downloaded.** Saffron reads every
|
|
173
|
+
page of a GitHub run's artifacts, not just the first 100, and downloads
|
|
174
|
+
exactly the `saffron-bundle*` artifacts it sized, one by one by name, on
|
|
175
|
+
GitHub and Azure. An artifact whose name it does not accept is neither
|
|
176
|
+
counted nor downloaded.
|
|
177
|
+
- **Doc strings in `.saffron` files stay as written.** A line such as
|
|
178
|
+
`StepSet: sample` or `StepSet sample` inside a `"""` or ```` ``` ````
|
|
179
|
+
doc string is literal test data now, no longer read as a step set
|
|
180
|
+
definition or call.
|
|
181
|
+
|
|
182
|
+
VS Code extension 0.6.1
|
|
183
|
+
|
|
184
|
+
- **Health's pass rate of recent runs** is the runner's (saffron-ai 0.9.8
|
|
185
|
+
or later): a heal whose proof failed does not pass.
|
|
186
|
+
- **Open Report and Open Trace on a CI run keep `saffron.ciMaxDownloadMB`**
|
|
187
|
+
when the run's results are no longer on disk (trace with saffron-ai
|
|
188
|
+
0.9.8 or later).
|
|
189
|
+
- **Run, Replay only and Headed above each scenario.** A click runs that
|
|
190
|
+
scenario alone, by name (`saffron run <file>:<name>`), as a task in the
|
|
191
|
+
project that owns the file. The file is saved first, and the panel's
|
|
192
|
+
replay-only and headed toggles apply as they do to every run. Lenses
|
|
193
|
+
and the side panel find scenarios in the file's own Gherkin language
|
|
194
|
+
(`# language:`), nameless ones too. Needs saffron-ai 0.9.8 or later; with
|
|
195
|
+
an older one in the project a click says so.
|
|
196
|
+
- **A CI run's yellow results read "pending review", not "healed".**
|
|
197
|
+
Yellow also covers first recordings and proposals not yet verified.
|
|
198
|
+
- **Each yellow or red CI scenario says whether the run verified it**:
|
|
199
|
+
"unverified" or "verified", and nothing when the run does not say.
|
|
200
|
+
|
|
201
|
+
JetBrains plugin 0.6.1
|
|
202
|
+
|
|
203
|
+
- **Health's pass rate of recent runs** is the runner's (saffron-ai 0.9.8
|
|
204
|
+
or later): a heal whose proof failed does not pass.
|
|
205
|
+
- **Run, Replay only and Headed above each scenario**, with LSP4IJ
|
|
206
|
+
installed. A click saves the files and runs that scenario alone, by
|
|
207
|
+
name, through a temporary run configuration made for it, so the run
|
|
208
|
+
widget runs it again. A saved configuration of the same name is left as
|
|
209
|
+
it is, and nothing from the Saffron template carries in, and a scenario
|
|
210
|
+
of the same name in another file gets its own. The lenses work while the
|
|
211
|
+
IDE indexes, and say when the project's saffron-ai is too old.
|
|
212
|
+
- **Right-click Run inside a scenario** runs that scenario alone, with or
|
|
213
|
+
without LSP4IJ, never reusing a lens's Replay only or Headed
|
|
214
|
+
configuration; outside one, or in a file in another Gherkin language,
|
|
215
|
+
it runs the whole file, as before. Both need saffron-ai 0.9.8 or later.
|
|
216
|
+
- **Open Trace works with every runner and keeps the download limit.**
|
|
217
|
+
With saffron-ai 0.9.8 it passes `--max-download` to `saffron trace`,
|
|
218
|
+
which downloads a run no longer on disk; with 0.9.7, which rejects the
|
|
219
|
+
option, it leaves it out.
|
|
220
|
+
- **Open Trace finds a Scenario Outline's example row** ("Login (example
|
|
221
|
+
1)") by the name the run shows.
|
|
222
|
+
- **CI yellow results read "pending review", not "healed"**, and each
|
|
223
|
+
scenario says "unverified" or "verified" when the run reports it.
|
|
224
|
+
|
|
8
225
|
## 0.9.7: CI results without importing
|
|
9
226
|
|
|
10
227
|
Released 2026-10-05.
|
package/README.md
CHANGED
|
@@ -73,7 +73,7 @@ node dist/cli/index.js -p examples accept --all
|
|
|
73
73
|
|
|
74
74
|
| Command | What it does |
|
|
75
75
|
|---|---|
|
|
76
|
-
| `saffron run [paths] [--headed] [--filter expr] [--rerecord] [--no-agent] [--strict] [--browser b] [--no-hooks] [--workers n] [--heal-model m] [--no-verify] [--no-reuse] [--model m] [--storage-state f] [--trace mode]` | Run features. Cached replays are deterministic; misses/failures escalate to the agent (unless `--no-agent`). Replay cross-browser with `--browser firefox\|webkit`, parallelize with `--workers N`, heal on a cheaper model with `--heal-model`, keep an execution trace with `--trace retain-on-failure`. Exit 1 on red (and on yellow with `--strict`). |
|
|
76
|
+
| `saffron run [paths] [--headed] [--filter expr] [--rerecord] [--no-agent] [--strict] [--browser b[,b...]] [--device name] [--viewport WxH] [--no-hooks] [--workers n] [--agent-workers n] [--heal-model m] [--no-verify] [--no-reuse] [--model m] [--storage-state f] [--trace mode]` | Run features. Cached replays are deterministic; misses/failures escalate to the agent (unless `--no-agent`). A path with a line or a name, `features/login.saffron:12` or `"features/login.saffron:Successful login"`, runs that one scenario. Replay cross-browser with `--browser firefox\|webkit`, parallelize with `--workers N`, heal on a cheaper model with `--heal-model`, keep an execution trace with `--trace retain-on-failure`. Exit 1 on red (and on yellow with `--strict`). |
|
|
77
77
|
| `saffron accept [files... \| --all] [--include-unverified] [--include-imported] [--with-feature-edit] [--propagate]` | Promote cache proposals to committed caches (`--all` skips UNVERIFIED ones unless `--include-unverified`, and ones imported from a CI run unless `--include-imported`); `--with-feature-edit` also rewrites the adapted steps in the `.feature` file (and keeps the cache in sync); `--propagate` applies the heal's locator fixes to every other cache using the same locator. One heal repairs N scenarios before they ever fail. No args: list pending proposals. |
|
|
78
78
|
| `saffron reject [files... \| --all]` | Discard proposals; the agent will try again next run. |
|
|
79
79
|
| `saffron export [-o file] [--branch name]` / `saffron runs` / `saffron import <bundles...> \| --run <id>` | Carry a CI heal to your machine: export packs the run's proposals, report, screenshots and traces into one bundle for CI to upload; `runs` lists the branch's CI runs (GitHub Actions or Azure Pipelines); import (of a file, or of a run with `--run`) checks the bundle against your checkout (repository, each proposal's binding) and files it there, ready for `saffron diff` and `saffron accept`. `--branch` also has CI commit the proposals to a new branch and open a draft pull request with their diffs. |
|
|
@@ -155,8 +155,7 @@ milestones, statuses and decision gates, and everything shipped so far in
|
|
|
155
155
|
its "Where we are" table.
|
|
156
156
|
Headlines ahead: API testing (reusable REST operations, fixtures and schema
|
|
157
157
|
validation), scenarios with two users in two browsers, a configurable
|
|
158
|
-
features folder,
|
|
159
|
-
IDEs), parallel recording and a browser matrix in one run, failure video,
|
|
158
|
+
features folder, failure video,
|
|
160
159
|
agent-blind secrets, LLM-less MCP replay, the Level-2 shared step library
|
|
161
160
|
(gated on Level-1 divergence data), and the long-term open-core SaaS layer.
|
|
162
161
|
|