Repository navigation
ci: count GitHub uploads and commit-pinned images as Before / After screenshots - #35
Conversation
The check told screenshots from other images with a badge denylist (hosts, path segments, badge SVG names, a branch-name exemption) that misclassified both ways. Now an image or video counts only when it is a file uploaded to GitHub, identified by its UUID in any of the forms body_html can hold: github.com/user-attachments/assets/<uuid>, the older github.com/<owner>/<repo>/assets/<user>/<uuid>, and the rendered private-user-images and user-images URLs. Badges, emoji, external and raw-file images never count. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars.
|
romchornyi-pitcrew
left a comment
There was a problem hiding this comment.
🟡 1 major (inline) · 6 non-blocking recommendations · no blockers — reviewed at 4f707afd85
| Level | Verdict | |
|---|---|---|
| 1 · Idea | ✅ valid | Replacing the badge denylist with a positive "is this an upload" rule ends the misclassifications #34's review rounds kept finding |
| 2 · Architecture | ✅ right layer | Same script and workflow (pull_request_target, base-branch script, rendered body_html); the workflow itself is unchanged |
| 3 · Completeness | ✅ complete | Template comment, CLAUDE.md, error text and docstring follow. No other doc mentions the rule |
| 4 · Code | 🟡 1 major | The rule rejects the commit-pinned screenshots this repo's authors have used, including every CLI/agent-opened PR |
Major (inline)
- — commit-pinned
DashUIKit/.github/scripts/screenshots-check.py
Lines 55 to 57 in 4f707af
raw.githubusercontent.comscreenshots (the form used in #10, #14, #19, #20) no longer count, and CLI/agent authors have no way to upload.
6 non-blocking recommendations
<picture>light/dark counts once — — GitHub's documented themed-image pattern (<source media="(prefers-color-scheme: dark)" srcset=…dark>+<img src=…light>) holds two uploads but counts one. Counting a<source srcset>upload inside a<picture>would let the template's "light and dark" be met that way. The PR documents the current choice; (reproduced: "found 1").- Extensionless rendered uploads —
—
RENDEREDrequires\.\w+, soprivate-user-images…/1/2-<uuid>(no extension) is not recognised. Making the extension optional costs nothing. (reproduced; whether GitHub renders such a URL is not verified) cloud.githubusercontent.com/assets/<user>/<n>/<uuid>.<ext>— — the pre-user-images upload host is a genuine GitHub upload but is not inUPLOAD_HOSTS, while the comment says every form counts. (not verified that such URLs still render)- Guidance says "images" only — — videos also pass, and the template comment says "at least two images". "images or videos" in both would tell an author with an animation change that a recording works.
DashUIKit/.github/scripts/screenshots-check.py
Lines 212 to 215 in 4f707af
upload()readability —— the chained ternary andDashUIKit/.github/scripts/screenshots-check.py
Lines 128 to 138 in 4f707af
match and next(g for g in match.groups() if g)returnNone, a falsy match or astrby short-circuit. A{host: pattern}lookup with an explicitif not match: return Noneandmatch.group(1) or match.group(2)reads more plainly.BEFORE_AFTERbacktracks quadratically (outside this diff) — — a heading of "before" followed by ~62k punctuation and no "after" took ~18 s locally. The 65,536-character body limit keeps it under the job's 5-minute timeout, so the worst case is a slow check for the author's own PR.before\W{0,20}(and|vs\.?|to)?\W{0,20}afterbounds it. (reproduced by the security lane)
Review details
First review of this PR. Levels 1–3 inline. Level 4 ran the general lane (/code-review high) and a security lane, because the script parses the PR author's description in pull_request_target. The security lane found no reachable sink: upload keys are never printed, malformed src values exit 0/1 without a traceback, and the upload regexes are linear. The verifier pulled body_html of #10, #14, #19, #20 and #23 to check the major, and ran the reported inputs through upload(). Dropped:
- a made-up UUID counts as an upload: documented in the docstring as a known gap, and a reader sees a broken image;
- missing test cases for the major and item 1: folded into those items.
🤖 Reviewed with Claude Code
An author without a browser (gh pr create, an agent) cannot upload attachments, and this repo's earlier PRs show screenshots as repository files pinned to a commit. Such a file counts now: raw.githubusercontent.com/<owner>/<repo>/<40-hex sha>/…, or github.com .../raw/<sha>/… and .../blob/<sha>/…?raw=…, keyed by commit and normalised path, with an extension the tag can show. Files on a branch still do not count. Also: each <source> of a <picture> counts (light / dark), extensionless rendered and cloud.githubusercontent.com uploads count, the guidance says images or videos, and the Before / After heading regex no longer backtracks quadratically. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Re: the review on |
romchornyi-pitcrew
left a comment
There was a problem hiding this comment.
✅ no blockers · no majors · 7 non-blocking recommendations — reviewed at 9b4dcee9ec
| Level | Verdict | |
|---|---|---|
| 1 · Idea | ✅ valid | Uploads plus commit-pinned repository files: positive rules, no denylist |
| 2 · Architecture | ✅ right layer | Unchanged |
| 3 · Completeness | ✅ complete | Template, CLAUDE.md, error text, docstring and PR body follow |
| 4 · Code | ✅ clean | All 98 cases pass at this head; #19 and #20 pass from their real body_html |
Closed since the last review (bot review on 4f707af)
- Major: commit-pinned repository files count (
raw.githubusercontent.com/…/<sha>/…,…/raw/<sha>/…,…/blob/<sha>/…?raw=…), keyed by commit and normalised path. Files on a branch and short SHAs still don't. - A light / dark
<picture>counts both variants. - Extensionless rendered uploads and
cloud.githubusercontent.comuploads count. - The guidance and template say images or videos.
-
upload()is rewritten asscreenshot()with explicit branches. - The
Before / Afterregex gaps are bounded (~1 ms on the 62k-punctuation heading).
7 non-blocking recommendations
- "link" vs "embed" —
and — the template and the failure hint say to link pinned files, but only embedded images and videos count;
DashUIKit/.github/scripts/screenshots-check.py
Lines 258 to 262 in 9b4dcee
[before](https://raw…/<sha>/before.png)renders as<a href>and finds 0. "embed ()" says what the check needs. - Any
<source>in a<picture>counts —— aDashUIKit/.github/scripts/screenshots-check.py
Lines 111 to 116 in 9b4dcee
<source media="(min-width: 800px)">plus the<img>is one image at two sizes, yet counts as two. Counting a<source>only when itsmediamentionsprefers-color-schemematches the light / dark intent. (reproduced) <video><source src>—— standard video markup with the file in aDashUIKit/.github/scripts/screenshots-check.py
Lines 105 to 116 in 9b4dcee
<source src>never counts, so two pinned.mp4files written that way find 0. Reading<source src>inside a<video>would cover it. (reproduced)- Pinned files are not checked to exist — — a made-up 40-hex SHA with two file names passes while both images are broken. Unlike uploads, repository files can be checked: one
DashUIKit/.github/scripts/screenshots-check.py
Lines 170 to 181 in 9b4dcee
gh api repos/<o>/<r>/contents/<path>?ref=<sha>per key would close it. Same class as the documented made-up-UUID gap. (reproduced) - Path normalisation is lenient —
— a trailing slash (
…/a.png/, a 404) still counts, anda%2Fb.pngfolds intoa/b.png.?raw=false/?raw=0on a blob counts; GitHub redirected these to the raw file in the last review's probe, so this one likely renders. (reproduced: keys returned) - The empty
()group — — it exists only to line uproute, sha, pathwith the github.com pattern. Named groups would drop the placeholder. - Fixture names a personal fork — —
DashUIKit/.github/scripts/tests/screenshots-test.py
Lines 152 to 154 in 9b4dcee
romchornyi/DashUIKit-fork/…/pr19/…where the other cases useo/r;o/r/<sha>/before-light.pngcovers the same thing.
Review details
Re-review of the bot review on 4f707af; the new diff is 9b4dcee. Levels 1–3 re-checked inline. Level 4 used the general lane (/code-review high) on the new commit, and the verifier ran the reported inputs through screenshot() and the parser. Dropped:
- "srcset may be proxied through camo": the author checked with GitHub's markdown API that a raw file in a
<picture>'ssrcsetis rendered as written; - a comma inside a
srcsetURL: unusual file names, and the<img>path is unaffected.
🤖 Reviewed with Claude Code
- A <picture> counts the images a light-mode and a dark-mode reader see: the first <source> with a srcset before the <img> whose media applies (none, all, screen, or that colour scheme), else the <img>. A screen-size <source> or a format variant no longer counts as a second image, and a <picture> with no <img> shows nothing. - GitHub strips a hand-written <video> unless its file is an upload, so commit-pinned files count only as images, and a <video> counts only an upload. - The template, CLAUDE.md and the failure hint say to embed pinned images with : a plain link does not count, and a video must be dragged in. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Re: the review on
Items 4 and 5 (made-up SHA, lenient path normalisation) are left as they are: both show up as broken images in the description. 118 cases pass. |
romchornyi-pitcrew
left a comment
There was a problem hiding this comment.
✅ no blockers · no majors · 6 non-blocking recommendations — reviewed at 53aa5aaa70
| Level | Verdict | |
|---|---|---|
| 1 · Idea | ✅ valid | Unchanged |
| 2 · Architecture | ✅ right layer | Unchanged |
| 3 · Completeness | ✅ complete | Template, CLAUDE.md, hint, error text and PR body follow the new rules |
| 4 · Code | ✅ clean | All 118 cases pass at this head; #19 and #20 still pass from their real body_html |
Closed since the last review (bot review on 9b4dcee)
- The template,
CLAUDE.mdand the hint say to embed pinned images with; a plain link doesn't count. - A
<picture>counts what a light-mode and a dark-mode reader see, so a screen-size<source>or a format variant is no longer a second image. -
<video><source src>: GitHub strips hand-written<video>tags that don't point at uploads, so pinned files count only as images and a<video>counts only an upload. - The fixture uses
o/r.
6 non-blocking recommendations
All six need unusual <picture> markup, and none of them can turn an ordinary light / dark pair into a wrong result.
- Catch-all media queries — —
DashUIKit/.github/scripts/screenshots-check.py
Lines 171 to 190 in 53aa5aa
only screenor(min-width: 1px)applies to every reader, butseen()treats it as not applying. A colour-scheme<source>after it is counted although nobody sees it. (reproduced:[z, y]) - Compound and negated colour-scheme queries —
— the substring test applies
(prefers-color-scheme: dark) and (max-width: 1px)to every dark reader. It also appliesnot all and (prefers-color-scheme: dark)to dark readers, which is the opposite of what it means. (reported by the general lane) - A struck-out
<source>—— it is skipped, but a browser still picks it. A struckDashUIKit/.github/scripts/screenshots-check.py
Lines 117 to 121 in 53aa5aa
<img>is kept as the end of the source list, so<source>should behave the same way. (reported by the general lane) - Nested
<picture>—— a single dict replaced the depth counter, so an innerDashUIKit/.github/scripts/screenshots-check.py
Lines 122 to 134 in 53aa5aa
<picture>overwrites the outer one's sources. (reproduced: the outer dark source is lost) - Unclosed
<picture>—— its images are only flushed onDashUIKit/.github/scripts/screenshots-check.py
Lines 132 to 135 in 53aa5aa
</picture>, so a description that ends inside one loses them.close()could flush it. (reported by the general lane) - Readability — and —
DashUIKit/.github/scripts/screenshots-check.py
Lines 107 to 110 in 53aa5aa
screenshot(src, image)reads as "is pinned" rather than "may be pinned" (allow_pinnedwould say it), and the"img" not in picturereturn belongs above the per-scheme loop.
Review details
Re-review of the bot review on 9b4dcee. The new diff is 53aa5aa. Levels 1–3 re-checked inline. Level 4 used the general lane (/code-review high) on the new commit. The verifier ran items 1 and 4 through the parser. Nothing was dropped.
🤖 Reviewed with Claude Code
Issue being fixed or feature implemented
The
Before / After screenshotscheck (#34) told screenshots from other images with a badge denylist: badge hosts,badge/badgespath segments, badge SVG names, and an exemption for GitHub hosts wherebadgecan be a branch name. Every review round on #34 grew the list, and each rule misclassified in both directions. The last bot review on #34 reproduced three cases:feat/badgesbranch dropped real screenshots;.github/badge/coverage.svgbadge counted as a screenshot;badge/folder dropped screenshots.The template asks authors to drag files into the table, and a dragged file is an upload. An author without a browser (
gh pr create, an agent) cannot upload attachments; this repo's earlier PRs (#10, #14, #19, #20) show screenshots as repository files pinned to a commit instead.What was done?
Uploads and commit-pinned files count. An image or video counts when it is a file uploaded to GitHub, identified by its UUID in any form
body_htmlcan hold:github.com/user-attachments/assets/<uuid>(the markdown form; GitHub leaves it in a<picture>and for some viewers);github.com/<owner>/<repo>/assets/<user>/<uuid>;private-user-images.githubusercontent.com/<user>/<n>-<uuid>[.<ext>]?jwt=…, the olderuser-images.githubusercontent.com/…and the oldestcloud.githubusercontent.com/assets/….Or, for images only, when it is a repository file pinned to a full 40-hex commit SHA (GitHub strips a hand-written
<video>whose file is not an upload, so a video must be dragged in):raw.githubusercontent.com/<owner>/<repo>/<sha>/…,github.com/<owner>/<repo>/raw/<sha>/…, or…/blob/<sha>/…?raw=…. Such a file is keyed by commit and normalised path, so a fork, a..segment or percent-encoding still counts once. It must be embedded (); a plain link does not count.The same upload in two forms counts once.
data-canonical-srcis read first, thensrc. Onlyhttps, and the whole path must match. A<picture>counts what a light-mode and a dark-mode reader see (GitHub's light / dark pattern): the first<source>before the<img>whose media applies, else the<img>.Never counted: badges, emoji, externally hosted images (directly or through camo), files on a branch or a short SHA, and
user-attachments/files/attachments.Removed: the whole badge machinery (
NOT_SCREENSHOT_HOSTS,BADGE_*,BRANCH_HOSTS,is_badge()) and the emoji class check.Docs: the error message, the template comment and
CLAUDE.mdname both ways: drag the files in, or link files pinned to a full commit SHA.Heading regex: the
Before / Aftergaps are bounded, so a long run of punctuation no longer makes the match quadratic.Known gap: an upload GitHub cannot resolve (a made-up UUID) still counts. A reader sees it as a broken image.
How Has This Been Tested?
python3 .github/scripts/tests/screenshots-test.py: 118 cases, all pass.private-user-images<img>, and a dragged video as a<details><video>block.fullmatch, the case folding, thesrcfallback, the attribute order or theuser-imageshost each fails a case.actionlintis clean.<picture>'ssrcsetis not proxied through camo./code-review highrounds were run until clean, before the PR and again after the bot review.Before / After
CI config only, no files under
Sources/.Breaking Changes
None. A description that passed with images hosted elsewhere or on a branch now needs uploads or commit-pinned files.
Checklist:
🤖 Generated with Claude Code