Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/browser-flags.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ We migrated from Puppeteer to Playwright because:

## Source of Truth

The authoritative reference for Chrome flags is maintained by the Chrome team:
The Chrome team maintains the reference for Chrome flags:

- **Primary**: [Chrome Flags for Tools](https://github.com/GoogleChrome/chrome-launcher/blob/main/docs/chrome-flags-for-tools.md) - Curated list for automation tools
- **Complete list**: [peter.sh/experiments/chromium-command-line-switches](https://peter.sh/experiments/chromium-command-line-switches/) - All Chromium switches
Expand Down
69 changes: 23 additions & 46 deletions docs/json-output.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ With `--wait`, includes comparison results:
"changed": 1,
"identical": 12
},
"approvalStatus": "pending",
"visual_review": { "state": "pending" },
"contextCommand": "vizzly context build abc123-def456 --agent --json --source cloud",
"exitCode": 1
}
Expand Down Expand Up @@ -289,13 +289,13 @@ Compact agent JSON:
"build": {
"id": "abc123",
"status": "completed",
"approval_status": "pending"
"review_state": "pending"
},
"baseline": {
"selected": {
"id": "baseline-build",
"name": "Approved Main",
"approval_status": "approved"
"review_state": "approved"
},
"selection_reason": "latest approved build"
},
Expand All @@ -319,7 +319,7 @@ Compact agent JSON:
"id": "cmp-1",
"screenshot_name": "Dashboard",
"result": "changed",
"approval_status": "pending",
"review_state": "pending",
"needs_review": true,
"screenshot": {
"id": "current-1",
Expand Down Expand Up @@ -390,13 +390,13 @@ Full build context JSON:
"build": {
"id": "abc123",
"status": "completed",
"approval_status": "pending"
"review_state": "pending"
},
"baseline": {
"selected": {
"id": "baseline-build",
"name": "Approved Main",
"approval_status": "approved"
"review_state": "approved"
},
"selection_reason": "common_ancestor",
"comparison_baseline_build_ids": ["baseline-build"]
Expand Down Expand Up @@ -469,7 +469,7 @@ Agent comparison JSON:
"id": "cmp-1",
"screenshot_name": "Dashboard",
"result": "changed",
"approval_status": "pending",
"review_state": "pending",
"screenshot": {
"url": "https://.../current.png"
},
Expand Down Expand Up @@ -523,16 +523,12 @@ Raw comparison JSON remains available without `--agent`:
"result": "changed",
"analysis": {
"diff_image_url": ".vizzly/diffs/dashboard.png",
"diff_regions": [],
"confirmed_regions": []
"diff_regions": []
}
},
"history": {
"similar_by_fingerprint": [],
"recent_by_name": [],
"hotspot_analysis": {
"confidence": "no_data"
}
"recent_by_name": []
}
}
```
Expand All @@ -551,11 +547,6 @@ vizzly context screenshot Dashboard --source local --json
"screenshot": {
"name": "Dashboard"
},
"confirmed_regions": [
{
"label": "Known header copy band"
}
],
"history": {
"recent_comparisons": []
}
Expand Down Expand Up @@ -643,7 +634,7 @@ vizzly builds --branch main --status completed --limit 10 --json
"changed": 1,
"identical": 12
},
"approvalStatus": "approved",
"visual_review": { "state": "approved" },
"createdAt": "2025-01-15T10:30:00Z",
"completedAt": "2025-01-15T10:32:00Z"
}
Expand Down Expand Up @@ -687,9 +678,7 @@ vizzly comparisons --build <id> --status changed --json
"status": "completed",
"result": "changed",
"diffPercentage": 0.042,
"approvalStatus": "pending",
"reviewState": "pending",
"visualReview": { "state": "pending" },
"visual_review": { "state": "pending" },
"viewport": { "width": 1920, "height": 1080 },
"browser": "chromium",
"urls": {
Expand All @@ -700,7 +689,7 @@ vizzly comparisons --build <id> --status changed --json
"honeydiff": {
"fingerprintHash": "00000000001ec127",
"regionCount": 12,
"projection": {
"details": {
"clusters": { "count": 12, "average_density": 0.81 }
}
}
Expand All @@ -717,9 +706,8 @@ vizzly comparisons --build <id> --status changed --json
```

`status` preserves the processing value returned by the API. Use `result` for
the visual outcome (`identical`, `changed`, or `new`) and `reviewState` for the
current review decision. Older responses that only provide `status` and
`approvalStatus` keep working with the same fields.
the visual outcome (`identical`, `changed`, or `new`) and `visual_review.state`
for the current review state.

Search by name across builds:

Expand Down Expand Up @@ -913,7 +901,7 @@ vizzly upload ./screenshots --wait --json
"failed": 2,
"new": 1
},
"approvalStatus": "pending",
"visual_review": { "state": "pending" },
"executionTimeMs": 9876
}
}
Expand Down Expand Up @@ -955,9 +943,9 @@ vizzly approve <comparison-id> --json
{
"status": "data",
"data": {
"approved": true,
"comparisonId": "comp_123",
"comparison": { /* updated comparison */ }
"eventId": "event_123",
"idempotent": false,
"review": { /* visual review */ }
}
}
```
Expand All @@ -972,10 +960,9 @@ vizzly reject <comparison-id> --reason "Unexpected regression" --json
{
"status": "data",
"data": {
"rejected": true,
"comparisonId": "comp_123",
"reason": "Unexpected regression",
"comparison": { /* updated comparison */ }
"eventId": "event_124",
"idempotent": false,
"review": { /* visual review */ }
}
}
```
Expand Down Expand Up @@ -1040,15 +1027,7 @@ vizzly status <build-id> --json
"newComparisons": 2,
"changedComparisons": 1,
"identicalComparisons": 12,
"reviewState": "pending",
"review": {
"pending": 3,
"approved": 12,
"rejected": 0,
"auto_approved": 0
},
"reviewFlow": "cricket",
"visualReview": { "state": "pending" },
"visual_review": { "state": "pending" },
"executionTime": 4500,
"scope": {
"organization": { "id": "org-1", "slug": "acme" },
Expand Down Expand Up @@ -1080,9 +1059,7 @@ vizzly status <build-id> --json
The status command reads the API status bundle directly. Processing counts,
comparison counts, conclusion, and review state stay separate; a pending review
is never treated as unfinished screenshot processing. Fields the API does not
provide are omitted instead of becoming client-authored zeroes. Legacy review
responses continue to expose `approvalStatus`, and legacy build links continue
to use the project-ID route when slug scope is unavailable.
provide are omitted instead of becoming client-authored zeroes.

### `vizzly init`

Expand Down
8 changes: 4 additions & 4 deletions skills/vizzly/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: vizzly
description: Inspect and explain Vizzly visual regression evidence, verify UI changes, troubleshoot local or cloud builds, adjust screenshot capture, or configure Vizzly CI. Use when a task mentions Vizzly, screenshot baselines, comparisons, Honeydiff, visual review, dynamic regions, or an existing Vizzly workflow.
description: Inspect and explain Vizzly visual regression evidence, verify UI changes, troubleshoot local or cloud builds, adjust screenshot capture, or configure Vizzly CI. Use when a task mentions Vizzly, screenshot baselines, comparisons, Honeydiff, visual review, or an existing Vizzly workflow.
---

# Vizzly
Expand Down Expand Up @@ -47,8 +47,8 @@ test workflow in charge of how the UI is exercised.
values.
- Do not approve, reject, comment on, publish, or replace evidence unless the
task explicitly asks for that mutation.
- Preserve thresholds, cluster sizes, signature properties, and dynamic-region
behavior unless repeated evidence justifies a change.
- Preserve thresholds, cluster sizes, signature properties, and other capture
settings unless repeated evidence justifies a change.
- Prefer deterministic fixtures and existing user journeys over hiding a diff
with broader tolerances or a new screenshot-only test.
- Report the comparison or screenshot identity, observed evidence, access
Expand All @@ -60,5 +60,5 @@ test workflow in charge of how the UI is exercised.
discovery, drill-downs, images, and TDD lifecycle.
- [SDK capture](references/sdks.md): add or change screenshot capture code.
- [Dynamic content](references/dynamic-content.md): investigate unstable
content, hotspots, or confirmed regions.
content and screenshot-specific tolerances.
- [Setup and CI](references/setup-ci.md): initialize Vizzly or change CI.
4 changes: 2 additions & 2 deletions skills/vizzly/references/dynamic-content.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ can cause a diff. They are possible explanations, not conclusions.
2. Record the visible region, recurrence, render metadata, and whether the same
change appears across builds or variants.
3. Prefer deterministic fixtures when they can remove irrelevant variation.
4. Treat hotspots and confirmed regions as server- or user-authored evidence.
This CLI exposes them for inspection, not authoring.
4. Use diff regions only to locate changed pixels; they do not approve a
comparison or explain why it changed.
5. Preserve `threshold` and `minClusterSize` unless repeated evidence
justifies a change.

Expand Down
48 changes: 1 addition & 47 deletions src/api/endpoints.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export async function getBuild(client, buildId, include = null) {
*
* @param {Object} client - API client
* @param {string} buildId - Build ID
* @returns {Promise<Object>} Canonical build status bundle
* @returns {Promise<Object>} Build status response
*/
export async function getBuildStatus(client, buildId) {
return client.request(`/api/sdk/builds/${buildId}/status`);
Expand Down Expand Up @@ -375,52 +375,6 @@ export async function searchComparisons(client, name, filters = {}) {
return client.request(endpoint);
}

// ============================================================================
// Hotspot Endpoints
// ============================================================================

/**
* Get hotspot analysis for a single screenshot
* @param {Object} client - API client
* @param {string} screenshotName - Screenshot name
* @param {Object} options - Optional settings
* @returns {Promise<Object>} Hotspot analysis data
*/
export async function getScreenshotHotspots(
client,
screenshotName,
options = {}
) {
let { windowSize = 20 } = options;
let encodedName = encodeURIComponent(screenshotName);
let endpoint = buildEndpointWithParams(
`/api/sdk/screenshots/${encodedName}/hotspots`,
{
windowSize: String(windowSize),
}
);
return client.request(endpoint);
}

/**
* Batch get hotspot analysis for multiple screenshots
* @param {Object} client - API client
* @param {string[]} screenshotNames - Array of screenshot names
* @param {Object} options - Optional settings
* @returns {Promise<Object>} Hotspots keyed by screenshot name
*/
export async function getBatchHotspots(client, screenshotNames, options = {}) {
let { windowSize = 20 } = options;
return client.request('/api/sdk/screenshots/hotspots', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
screenshot_names: screenshotNames,
windowSize,
}),
});
}

// ============================================================================
// Auth/Token Endpoints
// ============================================================================
Expand Down
2 changes: 0 additions & 2 deletions src/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ export {
createBuild,
finalizeBuild,
finalizeParallelBuild,
getBatchHotspots,
getBuild,
getBuildContext,
getBuildStatus,
Expand All @@ -52,7 +51,6 @@ export {
getPreviewInfo,
getReviewQueueContext,
getScreenshotContext,
getScreenshotHotspots,
getSimilarFingerprintContext,
getTddBaselines,
getTokenContext,
Expand Down
11 changes: 3 additions & 8 deletions src/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -1059,11 +1059,6 @@ contextCmd
'Maximum recent comparisons to return (1-50)',
Number
)
.option(
'--window-size <n>',
'Historical hotspot analysis window size (1-50)',
Number
)
.addHelpText(
'after',
`
Expand Down Expand Up @@ -1181,7 +1176,7 @@ program
.argument('<endpoint>', 'API endpoint (e.g., /api/sdk/builds)')
.option(
'-X, --method <method>',
'HTTP method (GET or POST for approve/reject/comment)',
'HTTP method (GET or POST for build comments)',
'GET'
)
.option('-d, --data <json>', 'Request body (JSON)')
Expand All @@ -1202,10 +1197,10 @@ Examples:
$ vizzly api /api/sdk/builds # List builds
$ vizzly api /api/sdk/builds -q limit=5 # With query params
$ vizzly api /api/sdk/builds/abc123 # Get specific build
$ vizzly api /api/sdk/comparisons/abc123/approve -X POST
$ vizzly api /api/sdk/builds/abc123/comments -X POST -d '{"content":"Looks good"}'
$ vizzly api /api/sdk/builds/abc123/comments -X POST -d '{"content":"Nice!"}'

Note: POST is restricted to approve, reject, and comment endpoints.
Note: POST is restricted to build comment endpoints. Use dedicated approve/reject commands for review decisions.
Most operations have dedicated commands (builds, comparisons, approve, etc.).
`
)
Expand Down
10 changes: 3 additions & 7 deletions src/commands/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,7 @@ import { createApiClient as defaultCreateApiClient } from '../api/index.js';
import { loadConfig as defaultLoadConfig } from '../utils/config-loader.js';
import * as defaultOutput from '../utils/output.js';

let ALLOWED_POST_ENDPOINTS = [
/^\/api\/sdk\/comparisons\/[^/]+\/approve$/,
/^\/api\/sdk\/comparisons\/[^/]+\/reject$/,
/^\/api\/sdk\/builds\/[^/]+\/comments$/,
];
let ALLOWED_POST_ENDPOINTS = [/^\/api\/sdk\/builds\/[^/]+\/comments$/];

function createApiCommandDeps(deps = {}) {
return {
Expand Down Expand Up @@ -89,14 +85,14 @@ export function validateApiRequest({ endpoint, method, hasData = false }) {

if (method !== 'GET' && method !== 'POST') {
errors.push(
`Method ${method} not allowed. Use GET for queries or POST for approve/reject/comment.`
`Method ${method} not allowed. Use GET for queries or POST for build comments.`
);
return errors;
}

if (method === 'POST' && !isAllowedPostEndpoint(endpoint)) {
errors.push(
`POST not allowed for ${endpoint}. Only approve, reject, and comment endpoints support POST.`
`POST not allowed for ${endpoint}. Only build comment endpoints support POST.`
);
}

Expand Down
Loading