Screenery

Docs / Testing and review

Reviewing a pull request's screenshots

Approve or reject each screenshot a pull request changed, one keystroke at a time, and see where the review stands. A review records a decision. It never moves @latest or any other channel.

The review workspace

Open the pull request's gallery in the console (the link in the pull-request comment goes there) and choose Review changes. The workspace lists every screenshot that is changed, added or removed; unchanged screenshots need no decision and are left out.

Your marks are drafts until you choose Submit review. Drafts are kept in your browser, so reloading the page keeps them. Submitting sends every draft as one review, with an optional summary. If the pull request pushed new screenshots after you opened the page, the submission is refused for the screenshots that changed, and the page says to reload.

Keyboard shortcuts

KeyDoes
YApprove, then go to the next screenshot nobody has decided
NReject, then go to the next screenshot nobody has decided
↓ / ↑Next / previous screenshot
⌘Z / Ctrl+ZUndo the last mark
?Show the shortcuts

A chip such as 3 / 12 reviewed counts your drafts and the decisions already recorded.

Review state

For its newest push, a pull request's review is in one of four states:

A screenshot whose every change is one your flake ledger ignores needs no decision by default: its bytes are new, but all of the change is known noise. An admin can make those need an approval too, on the project's Ignored page (Ignored changes in review).

One rejected screenshot makes the whole review a rejecting one. A later decision on the same screenshot replaces an earlier one.

New pushes keep earlier approvals

A decision is about the exact bytes you looked at, including a screenshot's dark or other themes. When you push again to the same pull request, every screenshot whose bytes did not change keeps its decision, so fixing a typo on one page does not send the whole pull request back for review. An approval also carries over when the bytes changed but the change is the same one, such as a header that moved the same 4 pixels again. Anything else whose bytes changed shows as unreviewed again, and a rejection only stays with the exact bytes it was made on.

The screenery check on GitHub

On a pull request, Screenery also reports the review state to GitHub as a check named screenery, on the pull request's newest commit. It appears in the pull request's checks list next to your CI jobs:

Review stateThe check
no-changesSuccess: no visual changes
approvedSuccess: every change approved
pendingAction required: changes waiting for review
rejectedFailure: a change was rejected

The check is posted when a push finishes and updated every time someone submits a review, so approving the last change turns it green without a new commit. A new push starts a new check on the new commit, and screenshots whose bytes did not change keep their decisions. Its Details link opens the review workspace. The check lists screenshots by name and counts only; it never shows an image, because anyone who can read the repository can read a check.

When one repository feeds more than one Screenery project, each project's check is named screenery/<project> instead, so they do not overwrite each other. The project's Access screen in the console shows the exact name under PR check.

The check needs three things:

Requiring approval before merge

To block merging until every visual change is approved, make the check required in the repository's branch protection:

  1. Open a pull request and let Screenery post the check once. GitHub only offers checks it has seen recently.
  2. In the repository on GitHub, go to Settings → Branches and edit the rule for your default branch (or add one), or use Settings → Rules → Rulesets.
  3. Turn on Require status checks to pass before merging, search for screenery (or screenery/<project>) and add it. In a ruleset, choose the Screenery app as the source of the check if GitHub asks.
  4. Save. From then on, a pull request with an unreviewed or rejected change cannot be merged until someone approves it in Screenery.

Two things to know before you require it. A pull request whose push never reaches Screenery (a fork, a workflow that skipped the push, a job without id-token: write) never gets the check, so GitHub waits for it. And if you later bind a second project to the same repository, the check's name changes to screenery/<project>: update the required check to match.

The check is the gate a person can clear. If you would rather the CI job itself fail on visual changes, use the visual-diff gate as well or instead.

Who can review

Reviewing needs the publisher role or higher in the organization, the same role that may promote a channel. A viewer can see the gallery and the review state, and can comment, but cannot approve or reject. Someone viewing a public project's gallery without signing in can see the screenshots but not review them. A publish or read token cannot review either: a review is always attributed to a person.

Reviewing with an agent

Review with Claude in the workspace opens Claude Code with a prompt that names the pull request, the project and the two tools it needs from the Screenery MCP server: screenery_gallery to read every changed screenshot and what is already decided, and screenery_review to record one decision per screenshot. Copy prompt copies the same prompt for any other agent. Nothing is sent from the page.

A review records who submitted it and how: from the console, the CLI, or an agent's MCP connection, with the agent's name when it gives one. The gallery and the review state show it, so you can tell your own decision from an agent's.

What a review does not do