Images
The screenshot pipeline
A gallery is the most persuasive part of a product site and the easiest place to leak something. Every image is inventoried, de-duplicated, reviewed, redacted on a copy and recorded.
The pipeline is mandatory, and it is ordered. Inventory first, then exact duplicates, then near-duplicates, then a visual review of every candidate, then redaction on a copy, then derivatives, then a manifest and a coverage matrix. A screenshot that skipped a step does not ship.
1. Inventory
The inventory lists every tracked image with its bytes, dimensions and SHA-256. Images are grouped by origin: captures shipped with the product, captures found in QA or verification material, and everything else such as diagrams, icons and logos.
Three kinds are excluded before review, and recorded as excluded: anything under an internal folder, anything that is not a UI capture, and anything below a usable resolution. The exclusion is a verdict, not a silent skip.
2. Exact duplicates
Identical SHA-256 hashes are one image. One is kept and the rest are recorded as duplicates of the kept id. The counts have to reconcile: inventory equals duplicates plus near-duplicates plus excluded-before-review plus reviewed.
3. Near-duplicates
A perceptual hash clusters images that look alike. The playbook uses a Hamming distance of four or less, with Pillow and imagehash in a scratch environment, never in the repository. The clearest representative of each cluster is kept: sharpest, most complete state, no cursor, no toast covering content.
The script that does this is scripts/shots_inventory.py. Its inventory stage is standard library only; cluster and sheets need Pillow, and cluster also needs imagehash.
4. Contact sheets and review
Contact sheets are built outside the repository, four columns by three rows, with each tile labelled by its inventory id. Every tile is reviewed. For any tile where sensitive content might be present, the full-size original is opened read-only and inspected before a decision is made.
The review checklist is specific: keys and tokens, passwords and filled password fields, mail and database credentials, connection strings, email addresses and personal names, invoices, project references, private IP addresses and hostnames, owner file paths, real third-party data, and internal file names that leak the process. Any hit makes the image a redaction candidate or an exclusion.
5. Verdicts
| Verdict | Meaning |
|---|---|
| INCLUDE | Clean and public-safe. |
| REDACT | Useful, with sensitive regions that can be covered cleanly on a copy. |
| EXCLUDE | Not used, with a reason: duplicate, near-duplicate, forbidden folder, not UI, too small, wrong content, broken layout, loading state, error state, email present, real data or unclear. |
The counts must reconcile. Reviewed equals INCLUDE plus REDACT plus EXCLUDE, and every candidate has a verdict. A candidate with no verdict is an unfinished review, not a pass.
6. Redaction
Redaction happens on a copy, never on the original. Solid rectangles cover the exact regions identified at full size, and each rectangle is recorded as x, y, width and height in original pixel coordinates. After redaction the derivative is opened again and checked: the sensitive text must be unreadable, and nothing else may be hidden.
A crop can reveal what the full image hid, so the full review runs again on the crop or the redacted derivative.
7. Derivatives
Each accepted image becomes two files: a full derivative at a maximum width of 1600 and quality around 80, and a thumbnail at a maximum width of 640 and quality around 75. Slugs are descriptive and lowercase, with no internal ids or numbering. The budgets are per-file and per-directory, and a derivative that exceeds them is re-encoded, not shipped.
8. The manifest
site/shots/manifest.json records every derivative: slug, area, alt text, caption, source path, verdict, whether it was redacted, its width and height, and any callout coordinates. The builder reads the manifest and refuses to render an image that has no alt text or no caption.
The source path is for traceability only. The builder never copies an original into the site; it copies the reviewed derivative.
9. Alt text and captions
Every image has both. The alt text describes what the screen shows and what a visitor learns, in one or two plain sentences, with no secret, path, email, project reference or internal jargon. The caption is one sentence: what it shows and why it matters. An empty state, a loading state and an error state each get a caption that says so; none of them is captioned as if it showed data.
10. Coverage
A coverage matrix maps every feature area to the page that shows it and the number of approved images. A feature area with no approved image shows no image slot at all. There is no empty frame waiting for a picture, and no caption written for an image that does not exist.