CI runs in your IDE
The VS Code extension and the JetBrains plugin list your branch's CI runs
in the Saffron panel, show what a run found, and import a run's heals with
one click: from a red build to an accepted repair without leaving the
editor. Both call the same runner commands, saffron runs --json,
saffron report --run <id> --json and saffron import --run <id> --json
(§19), so GitHub Actions and Azure Pipelines work the same in
both, and neither editor sees a token.
Before you start
- The pipeline uploads a bundle. A run can be imported when its job
ran
saffron exportand uploaded the result as an artifact namedsaffron-bundle*(§19, In CI). Importing a run without one says so. - The CI's own command-line tool, signed in, on your machine. The
GitHub CLI for GitHub Actions (
gh auth login); the Azure CLI with its azure-devops extension for Azure Pipelines (az extension add --name azure-devops, thenaz login, orAZURE_DEVOPS_EXT_PAT). The repository's host picks the service; a GitHub repository built by Azure Pipelines names it in the config (ci, §14). - A
saffron-aiwithsaffron runsin the project. With an older one the list says to update it (npm i -D saffron-ai@latest). - JetBrains: a trusted project. In a project the IDE does not trust, the tab runs nothing and says so.
Where to find it
- VS Code: the Saffron icon in the activity bar, view CI runs. A run's row has two inline actions, Import this run's proposals (the cloud icon) and Open the run in the browser. The view's title bar has Refresh CI runs, and the project picker when several projects are open. The view's heading names the project, the CI service and the branch it lists.
- JetBrains: the Saffron tool window (right side), tab CI Runs. Its toolbar has Import Run, Open in Browser and Refresh. Import and Open act on the selected run, or on the run a selected row belongs to.
Reading the list
One row per run of the current branch, the 20 newest first:
CI #48 failure · 12 min ago · 2 proposals pending here
CI #47 success · 3 h ago
- Name and number: the workflow (GitHub) or pipeline (Azure), and the run's number in it.
- Outcome: success, failure, cancelled, partial (Azure's partially succeeded), or queued and in progress while it runs, with a matching icon.
- When it was started.
- Proposals pending here: how many proposals in your checkout came from this run and still wait for a decision.
Hover a row for the commit or pull request title, the attempt when the run was re-run, what started it (a push, a pull request, a schedule), and the branch and commit it tested. On Azure Pipelines a pull request build is listed under the branch it validates, and only this repository's builds are listed.
The list loads when the view first shows, when you switch projects and when you press Refresh. VS Code also reloads it when you switch branches; in JetBrains, press Refresh after a checkout. It does not reload as files change: each load asks the CI over the network.
When the list cannot be read, a single row says why:
| Row | Means | What to do |
|---|---|---|
| CI tools not set up | gh or az is not installed, or az lacks the azure-devops extension |
Install it; the row's text says how |
| Not signed in | The tool is installed but not signed in | gh auth login, or az login or AZURE_DEVOPS_EXT_PAT |
| Repository not recognized | The tool cannot tell the repository, or the Azure DevOps organization and project | Open a clone whose remote is the CI's repository, or set ci.organization and ci.project (§14) |
| CI runs unavailable | Anything else, such as a saffron-ai without saffron runs |
The row's text says what |
Viewing a run's results
Open a run's row (expand it in VS Code, select it in JetBrains) to see
what it found without importing anything. The IDE runs saffron report --run <id> --json (§19) in the background, which you can cancel, and
shows the results under the run:
- a totals line (green, yellow, red);
- one row per red or yellow scenario, with the failing step and the first line of its error; hover for what the agent changed;
- "N proposals: M importable", and under it each proposal, with why it would not import (a scenario changed since the run, say);
- Open Report, the run's report page in your browser, and Open Trace on a scenario that kept a trace: the replay, view only, with no Accept or Reject.
The results panel (saffron-ai 1.0.0 or later) shows the same run as
one page: the totals, then each failed and pending-review scenario as a
card with its screenshot (click it to see it whole), failing step, error
and what the agent said, then what passed, then the proposals. Its
buttons do what the rows do: Open trace, Open scenario (the
feature file at that scenario), Open report, Open run page and
Import; after an import the page loads again with what is importable
now. In VS Code, Show Results (the graph icon on a run, or on its
totals line) opens it in an editor tab. In JetBrains it is below the
runs in the CI Runs tab, for the selected run. Viewing the run writes
the page beside its report, and both editors show that one file
(saffron report --run <id> --panel prints it too).
On GitHub the list also says what each run left before you open it: results and their size, summary only, results expired or no results; a run with nothing to view does not expand.
A run with no results says why in one row: still running, expired, or no
bundle uploaded (put if: always() on the export and upload steps to
keep every run's results). A run whose results are over the size limit
asks before downloading (VS Code saffron.ciMaxDownloadMB, and the same
setting in JetBrains, default 100 MB). Nothing in your checkout changes
until you import; the download is kept in .saffron/ci/ and the import
reuses it. Viewing needs saffron-ai 0.9.7 or later.
Importing a run
Import (VS Code: the cloud icon on the run; JetBrains: select the run,
then Import Run) runs saffron import --run <id>. It downloads the
run's Saffron bundles, every shard of a sharded run, and imports them into
this checkout with every check of §19: nothing is written from a bundle
that comes from another repository or is damaged, and a proposal that
does not fit your files is not imported. VS Code shows the import in a
notification you can cancel. Either IDE stops an import that takes over
15 minutes, and says so.
When it finishes, the run opens onto what happened:
- Warnings first, such as a checkout at another commit than the one the run tested (with how far apart they are), or a pull request build that tested a merge commit.
- Each proposal: imported (in VS Code, click it to open its diff), already pending here, already accepted here, or not imported with the reason, such as a scenario or recording that changed since the run.
- No proposals in this run when it filed none. Its report, screenshots and traces are imported all the same.
A message counts what is ready for review; in VS Code, its Show proposals button opens the Proposals view. Importing the same run again changes nothing and says so. In VS Code, Refresh CI runs clears these results once the new list is in.
Reviewing what you imported
An imported proposal is an ordinary pending proposal that says where it came from:
- Proposals marks it "from run 901". Hover it (VS Code) or select it (JetBrains) for the run and its link, and the branch, commit and shard it was made at. Read its narrative, proof-replay verdict and diff as for a heal of your own (§5).
- Last run shows the imported run: the scenarios that failed or healed, with the step that failed. Open the screenshot of the page at that moment, or Open Replay to step through the run's trace (§18).
Accept them with Accept selected once you have read them. Accept all
leaves imported proposals out and says how many it skipped; in a
terminal, saffron accept --all --include-imported takes them. Accept
checks each proposal's binding again, so one whose scenario or recording
changed since the import is refused rather than accepted unseen. The new
recordings are then an ordinary change in your working tree: commit and
push them with the IDE's version control (VS Code's Source Control view,
the JetBrains Commit window). Nothing is pushed for you, and your next
local run replaces the imported one under Last run, as any run does.
With several projects open, the list is the shown project's, the same one the rest of the panel shows, and an import goes into the project it was started in, even if you switch while it runs.