AI Summaries
Most visual diff tools give you a red-pixel overlay and leave you to figure out what changed. RegressionBot compares the two pages' DOM trees, groups the changed elements into labeled regions (A, B, C…), and writes one line per region that quotes what changed — so you can read a six-line summary instead of hunting through screenshots.
Storage Configuration
Learn how to configure Pulse to preserve visual snapshot records and perform comparisons across pipeline runs.
Configure Pulse to preserve visual snapshot records securely in Csecure cloud storage and comparing from Dstored baseline images on future pipeline runs.
Lock parameters during a run to prevent visual baseline regression drift across environments.
The new version of the page. Yellow boxes A–F mark the changed elements. Each box matches one line in the summary below.
How it Works
After capturing both pages, RegressionBot runs three steps to produce a summary. The screenshots are kept as pictures for you to look at. Nothing compares their pixels.
Compare
The two pages' DOM trees are matched element by element. Every changed element is recorded: new text, a changed style, a new image or link target, or an element added, removed or moved. Changes that sit next to each other are grouped into one region.
Label
Each region gets a letter (A, B, C…) in page order. The same letter marks the region in the inspect view and in the email's picture, so you always know which line refers to which part of the page.
Summarize
Each region's line is written from the DOM itself: the words before and after, the style that changed, or the element that was added or removed. Quotes are the page's own text. A short one-line overview of the whole page goes on top. No AI model looks at the screenshots.
Example Summary
The regressionbotSummary field is a structured JSON array — one element per labeled region, each with a label (the region letter) and a text line. The first item has an empty label: it is the one-line overview. Below is an example for the six-region job shown above.
- –Storage wording updated, an "Admin Dashboard" link and a "Project Configuration Locking" section added, and the logo and one contents link restyled.
- A"✓" → "■"
- Badded "Admin Dashboard"
- C"records securely in S3 vaults" → "records securely in secure cloud storage"
- D"comparing from stored S3 images" → "comparing from stored baseline images"
- Eadded "Project Configuration Locking · Lock parameters during a run to prevent visual baseline regression drift across…"
- Fcolor: rgba(255, 255, 255, 0.3) → rgb(52, 211, 153)
In most cases, reading the summary is enough to approve or escalate a job. To see a change on the page, open its diffUrl. It goes to the inspect view, which outlines every change on both versions.
Content Pushed Down
Adding or removing content moves everything below it. A new block added mid-page pushes the rest of the page down. Pixel-diff tools can't tell this shift from a real change.
RegressionBot matches elements by what they are, not by where they sit. An element that was only pushed down by a change above it is the same element with the same text and styles, so it is not a change.
Other tools
Flag the entire lower half of the page as changed — often dozens of regions — because every displaced element is in a different pixel position than the baseline.
RegressionBot
Reports only the inserted content, as region A. The elements below it moved down but did not change, so they are not reported.
Storage Configuration
Learn how to configure Pulse to preserve visual snapshot records and perform comparisons across pipeline runs.
Pulse supports automatic layout comparison checks. The parser scans sitemaps and evaluates page structures recursively.
Pulse now runs screenshot capture in parallel across serverless cloud environments for faster CI pipelines.
This paragraph sits below the insertion point and shifts downward when new content is introduced above it. It is unchanged.
A paragraph was inserted mid-page. One region (A) is labeled. The content below it shifted down but is not flagged as a change.
Automated AI Verdicts
A regression is not always a bug. It is often exactly what the code change set out to do. Pass the intent behind the change as runContext — the PR title and description, or the commit message — and every changed page is judged against it. Pass no intent and nothing is judged.
The one field a CI step reads
intentAssessment.decision, on the job, has four values:
pass— every changed page is accounted for by the intent.review— a page needs a person, or was never assessed.fail— a page contradicts the intent.not_judged— the run carried no intent, so nothing was assessed. A scheduled run gets its intent from the project's standingintentfield; without one, every scheduled run lands here.
Gate on equality with pass, not on inequality with fail. That single comparison stops the build on review and not_judged too, which is what you want in both cases.
if (summary.intentAssessment.decision !== 'pass') process.exit(1);The counts beside it — bugCount, intentionalCount, noiseCount, needsReviewCount, allAccountedFor and a one-line summary — explain how the decision was reached. Report them; do not gate on them.
Per-page verdicts
Each changed page carries a verdict whose decision is one of intentional, bug, noise or needs_review. Two rules govern reading it:
- A changed page can carry no
verdictobject at all. That page was never assessed. Treat it asneeds_review, never as a pass. minConfidenceruns from 0 to 1, so compare it against a fraction —0.8, not80.
What forces a review: the engine routes a change to needs_review whenever it cannot claim to understand it — a region it could not describe beyond "visual difference in a region with no captured text", a page whose capture looks like a render failure, and any whole-page change (those are capped below the confidence a clean verdict needs). One such page turns the job's decision into review. A gate that passed last week can therefore fail this week with no change to your site, because the engine got more careful rather than because the page got worse.
The field-by-field reference — basis, coveredBy, how to write an intent, and how to approve only the pages the verdict cleared — lives in Intent and the Verdict.
Parsing regressionbotSummary: it is an array of { label, text }, where label is the region letter shown in the inspect view. The one-line overview, when present, comes first with an empty label, and a whole-page change is described by a single item with an empty label, so code that keys on the label must tolerate one. Results from before September 28, 2026 were described from screenshots and may carry two fixed strings — one for a region with no captured text, one for an image whose source and dimensions are unchanged — both meaning "look at this yourself". Jobs created before v2.1 carry a plain string in this field instead of an array.
Using Summaries
SUMMARIZING phase of the job. The job will remain in SUMMARIZING status until all visual summaries are fully generated. Once complete, the job transition service marks the job status as COMPLETED and the summaryStatus as COMPLETE simultaneously. If the job fails, the status will move to FAILED.The regressionbotSummary field is available wherever job results are returned.
waitForCompletion() waits for both the job and the AI summary to finish before returning. Access regressionbotSummary on each regression result:
import { RegressionBot } from '@regressionbot/sdk';
const client = new RegressionBot(); // reads REGRESSIONBOT_API_KEY from env
const job = await client.runProject('my-project');
await job.waitForCompletion(5000, undefined, { waitForSummaries: true }); // waits for both job and AI summary
const summary = await job.getSummary();
for (const r of summary.regressions) {
// Typecast during transition period to avoid compilation errors on legacy SDK type declarations
const aiSummary = (r.regressionbotSummary as unknown) as Array<{ label: string; text: string }> | undefined;
for (const item of aiSummary ?? []) {
// item.label = "A" | "B" | "" (empty for wholesale changes)
// item.text = single-sentence description
console.log(item.label ? `${item.label}: ${item.text}` : item.text);
}
}Short Screenshot URLs
To make reviewing visual summaries as frictionless as possible, RegressionBot returns shortened, clickable redirect links for all screenshot fields (such as baselineUrl, currentUrl, and diffUrl).
Instead of cluttering your terminal logs or CI pipeline stdout with 600-character pre-signed URLs, you receive clean, 50-character clickable links:
Links are valid for 1 hour after generation — long enough to paste into a PR comment, Slack message, or CI log and open later. Clicking the link generates a fresh pre-signed image URL on the fly, so there is no separate expiry to track.