visual-pr
Skill2 starsChanged 50 days agoMITView on GitHub
Write pull requests a reviewer can understand in one scroll. A short human summary, then before/after stills, GIFs and slowed-down frame strips made for the PR from synthetic fixtures, each with a caption naming the state change, then the details. Use whenever you open a PR, write or rewrite a PR description, or are asked for "before/after", screenshots, a GIF or a demo of a change. It matters most for UI, animation, rendering, color, layout, performance and UX changes, and it still applies to backend work, where the visuals are diagrams, tables and output diffs.
- Commits and pushes
5 other projects use a version of this.
Adopted by
Most recently taken up by
- n1kben/agentstheir own version, first seen Nov 8, 2026
- mmnavarr/overseertheir own version, first seen Oct 15, 2026
- jellydn/my-ai-toolstheir own version, first seen Oct 13, 2026
- prefapp/skillstheir own version, first seen Oct 4, 2026
- thoriqakbar0/skillstheir own version, first seen Aug 19, 2026
Other versions5 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 5
- jellydn/my-ai-tools★ 127same name
- mmnavarr/overseer★ 94same name
- thoriqakbar0/skills★ 2same name
- prefapp/skills★ 1same name
- n1kben/agents★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
The file
We split this skill into the rules it gives an agent
7 rules. 0 are common, 4 are distinctive, and 3 are very specific to this project.
0
Common
Most of the 5 edited versions we read keep these.
4
Distinctive
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 54
Before adding a visual, write down the reviewer question it answers ("does the naive fix also work?", "does the easing overshoot?").
- Very specific: line 68
Write every image as markdown, !alt, including inside tables.
- Very specific: line 83
Put labels on the images too.
- Distinctive: line 85
When a visual doesn't show much, say so plainly: "the before/after transcripts are close to identical, and I am not going to claim …
- Distinctive: line 89
Do this while you build, not after.
- Distinctive: line 128
When none of these fit, build a tiny single-file visualizer (a token bucket filling, a queue draining, replicas converging) fed by the real …
- Very specific: line 134
When you're describing someone else's PR, keep what you ran apart from what the author reported ("cargo test --workspace passed (author's …
“Common” is measured: kept by at least 6 in 10 of the 5 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Visual PRs
A reviewer should understand what changed and see that it works before reading a single line of the How. Words alone don't do that. Evidence you made for the PR does: the same input rendered on main and on the branch, the motion slowed down until the easing can be judged, the edge cases cropped and zoomed in, and the numbers measured.
The reference PRs this skill is distilled from are cristicretu/diri #459, #460, #464, #465 and #476. references/examples.md walks through what each one did.
The shape of the body
<1–2 paragraphs, plain words: what was wrong or missing, as a user feels it,
and what is different now. Nothing else above the first figure.>
## Before and after (or name what is shown: "Hovering a session")
**Cross-project hover.** <caption: the input, what differs, where to look>
| main | this branch |
|---|---|
|  |  |
**Moving the pointer.** <caption>
 (one composited main|branch GIF)
**Slowed down.** <caption: "one tile per 8 ms; top row loses focus, bottom gains it">

<details><summary>Light theme</summary> …same table… </details>
## Edge cases (most PRs: a table of case | how checked | result)
---
## How (the decisions that aren't obvious, and what you tried and dropped)
## Verification (real numbers and commands; paste results, not claims)
## Not in this PR (the scope you drew, and anything you noticed but left)Rules for each part:
- Summary (max 2 paragraphs). Write it the way you'd explain the change to a colleague at their desk. Name the concrete symptom ("under a slow drag the text sat still for 15 px of finger travel and then jumped"), not a category ("improves scrolling"). If you checked the premise before building and it turned out weaker than expected, say so here. Being honest about that earns more trust than anything else in the PR. No headers, no bullets, no file names. Hard cap: 120 words across both paragraphs. Count them. Write for someone who hasn't read the issue or the code: say what a user saw and what they see now. Internal names (lock types, function names, error codes) go in How, unless one of them is the symptom itself.
- Visuals, as figures. Group them under one heading straight after the summary. Each figure gets a bold 2–5 word title, then one caption sentence saying what input was replayed, what differs, and where to look, then the media. The caption explains the state change so the image doesn't have to be decoded. See "Captions" and "Layout" below.
- Details, after the visuals. Use only the sections that add something. How covers the non-obvious decisions and the alternatives you rejected, with the reason for each. Verification gives measured results. Not in this PR marks the scope. If the details run long, put Verification inside
<details><summary>Verification</summary>…</details>so the page stays scannable. - Optional sections that earn their place when they apply: Review first (the 2–4 spots where you're least sure, which also tells the reviewer where to start), Merge notes (conflicts with open PRs), and Tests changed, and why (every existing test you modified, with the reason).
Length. The reference PRs run to about 1,300 words, which is too long. Aim for a summary under 120 words, captions of one sentence, and bullets of one or two lines. Cut any sentence that repeats what an image already shows. Keep the numbers and the rejected alternatives, because those are the parts reviewers can't get anywhere else.
Every visual has to earn its place
Before adding a visual, write down the reviewer question it answers ("does the naive fix also work?", "does the easing overshoot?"). If you can't, cut it. If deleting it loses nothing the text already says, delete it. Two strong figures beat six adequate ones.
- A diagram is only for structure that prose handles badly: a cycle (a deadlock is a 3-node wait-for loop, not a 10-message sequence diagram), a race interleaving, or a topology that changed. Use the smallest form that shows it. Don't draw an "after" diagram when the fix is "that edge is gone". Say it in one sentence.
- Raw output (
ps, logs, EXPLAIN) goes in only when a claim rests on it, trimmed to the lines that matter, usually inline or folded. - A variant that repeats a point (the light theme when dark already shows it, a second device) goes in a
<details>block, unless the variant is the point.
Layout: figures, not pasted images
- Pairs go in a table. Column headers replace the burned-in labels, and the images line up.
| main | this branch |, one row per state or theme, with a first label column if there are several rows. Upload the two panels as separate images for this. It suits narrow subjects (a sidebar, a phone screen, a component, a crop), since each column renders about 440 px wide. - Several items described by the same attributes get a table, not bullets. Examples: three or more flakes with root cause and fix, endpoints with before/after latency, themes with contrast ratios. If you catch yourself writing the same sentence shape three times, it's a table.
- Wide subjects (a full window, a terminal, a chart) get one stacked composite at full width. A table would shrink them until they're unreadable.
- Motion comparisons stay one composited GIF. Two GIFs in two cells don't start in sync.
- Never two images in a row with no text between them, except inside a table.
- A
---between the figures and## Howseparates what to look at from what to read. - Write every image as markdown,
, including inside tables.--attachonly rewrites markdown references, so<img src="./…">won't upload in place. To pin a width, seereferences/hosting.md.
Captions that explain the state change
A good caption answers three things: what input produced this, what differs between the panels or frames, and where to look.
| Weak | Strong |
| Before and after | Same scripted fling replayed on main (top) and this branch (bottom): main holds still, then jumps a row |
| Checkout page | Cart with 40 items and a 60-character product name, iPhone SE width: the total used to wrap under the button, now it stays on one line |
| GIF of the animation | Opening the sheet, then dragging it halfway and letting go. On main it snaps shut; now it settles back open |
| Frame strip | Slowed down, one tile per 8 ms, times since the click. Watch the badge: it scales from 0.9, never from 0 |
| Loading state | Network throttled to 3G: the skeleton now holds the exact layout of the loaded list, so nothing shifts when data arrives |
| Dark mode screenshot | Look at the divider under the header: invisible on main in dark mode, a 1 px line now |
Put labels on the images too. Each panel gets a caption bar saying what it shows (main: total wraps under the button / this branch: one line at 320 px), and each strip tile gets its time (+16 ms) or value (64%). A reader skimming only the images should still get the story.
When a visual doesn't show much, say so plainly: "the before/after transcripts are close to identical, and I am not going to claim otherwise." If a fixture exaggerates something to make the mechanism visible, label it in bold: This is not what ships.
Workflow
Do this while you build, not after. The frames are also how you judge your own work. The reference agents found real bugs (a progress bar turning pink, yellow drifting olive, invisible final animation frames) only because they looked at them.
- Plan the shots as soon as the change is understood. List the states and transitions a reviewer needs to see: before vs after, every state involved (focused/unfocused, empty/full, light/dark), the transition between states, and the edge cases (wide glyphs, long text, inverse colors, error state, small screen). Check the premise first: render
mainand confirm the problem looks like you were told it does. Then list the edge cases and blast radius: the awkward inputs to the new thing, and every existing flow that goes through the code you changed. Put a capture of those flows in the plan too. - Build a synthetic fixture that is realistic and deterministic. See
references/fixtures.md. Real-looking content (a transcript, a diff, a list with long names), fixed data, fixed size, and content that describes the feature itself where that's natural. - Capture deterministically. Freeze or fake the clock and step it frame by frame, so every frame is exactly reproducible. Screen-record wall-clock time only when stepping is impossible, and say so in the caption. Render
mainfrom the same fixture and input script: a second worktree on another port or simulator, or the branch with the feature switched off (say which).references/capture.mdhas recipes for web and Next.js (Playwright fake clock, Web Animations scrubbing), iOS (simctl, status-bar override, Core Animation scrubbing, ImageRenderer), Android, React Native, Electron and desktop, custom renderers, and CLIs. - Compose with the bundled scripts (Python 3 + Pillow, ffmpeg; matplotlib for charts; Node + Playwright for web):
scripts/web-frames.mjs: web stills and stepped animation frames on a frozen clock, for any framework, with--probeto log a value per frame.scripts/compare.py: labeled before/after panels, stacked or side by side. Pass frame directories instead of files to get a side-by-side GIF.scripts/strip.py: frame strip with+N mslabels, a crop of the region that moves, and nearest-neighbour zoom.scripts/gif.sh: palette-optimized GIF with slow-motion playback, a held last frame, and a warning over 4 MB.scripts/check-body.sh: after posting, fails if any local./path wasn't uploaded or any image link doesn't load.scripts/chart.py: backend charts, main vs branch on one scale. Trace waterfalls and worker lanes (plus a replay GIF), latency distributions, series with a shaded fault window. Details and budgets are inreferences/media.md.
- Look at every image with your image-reading tool before shipping it. Check it shows what the caption claims, the labels are readable at about 900 px wide, nothing is cropped wrong, and the motion reads the way you intended. If you changed something after looking (an easing, a duration, an opacity), put that in How. It's some of the most useful content in the PR.
- Attach the media; don't commit it. This works exactly like pasting a screenshot into GitHub:
gh pr createorgh pr editwith--attach ./file.png(gh ≥ 2.99) uploads each file to GitHub's attachment storage and rewrites the matchingin the body to its URL. Nothing touches git, and on a private repo only people with access can see it. A repo's own media rule (inAGENTS.md,CLAUDE.md,CONTRIBUTING.md) wins. Fallbacks for when--attachisn't available, and what it does and doesn't rewrite, are inreferences/hosting.md. - Write the body to a file only you use (
body=$(mktemp -t pr-<number>-body.XXXX)), next to the media, referencing each file as./name.png. Other agents may share your scratch directory, and a sharedbody.mdcan end up posting one PR's description on another. Check that the file's first line is the summary for this change. Then run, from the media folder,gh pr create --title … --body-file "$body" --attach ./a.png --attach ./b.gif …, orgh pr edit <n> --body-file "$body" --attach …for an existing PR. Finally, runscripts/check-body.sh <n>: it fails if a./path wasn't uploaded or a link doesn't load. Also confirm withgh pr view <n> --json titlethat you edited the right PR.
Edge cases and blast radius
Most PRs need this; copy or token-only changes don't. Regressions rarely come from the feature itself. They come from existing flows that go through the code you changed, and from inputs to that code changing mid-session. A real example: a sharing feature hid the chat view behind an ownership query "whenever the chat exists". A brand-new chat only starts to exist once its first answer is saved, so every new chat reloaded behind a spinner right after its first reply. That shipped with 300+ passing tests, all of them about sharing.
- Blast radius. Find every reader of each condition, flag, query or state you changed. For each existing flow, especially the ones your feature doesn't target, say what it did before and what it does now.
- Transitions. Walk each input through its changes while the screen stays mounted: new → saved, loading → refetching, signed out → in, flag off → on, empty → first item. A frame strip across the transition shows flashes and remounts.
- Inputs. Frontend: hover and focus states, long text and translations, RTL, empty and huge lists, 320 px, dark mode, reduced motion, keyboard, slow network. Backend: null and huge data, other user or anonymous, concurrent and repeated requests, old rows, flag on/off.
- Show it as an
## Edge casestable (case | how checked | result), with small figures for the visual ones. Anything you didn't check goes under Not in this PR.
Checklists and the table format are in references/edge-cases.md.
Backend and full-stack changes
A backend PR has to make a screen for the behavior, with the same imagination as a UI PR. The equivalent of a synthetic UI is a throwaway visualization of real data from a scripted run against main and the branch, drawn on one scale:
- Fewer or faster calls: a trace waterfall, 14 stacked queries above, 2 below.
- Latency: an ECDF with p50/p95/p99, from the same load script on both builds.
- Concurrency, queues, locks: worker lanes, plus a replay GIF where a "now" line sweeps across and jobs visibly queue on main and run in parallel on the branch.
- Resilience: a fault-injection timeline with the outage shaded. In-flight requests pile up on main and fail fast on the branch.
- Memory/CPU: soak-test series and flame graphs. Queries: EXPLAIN before/after with the changed node called out.
- Contracts and data: request/response or sample rows before/after in a
diffblock, and an ER diagram for migrations. - Flows, cycles, state machines: the smallest Mermaid diagram of the real participants, only when structure is the point (a deadlock as a 3-node wait-for loop). Usually draw only the broken state.
- When none of these fit, build a tiny single-file visualizer (a token bucket filling, a queue draining, replicas converging) fed by the real code's event log, and capture it like any UI.
If a backend change has an effect a user can see (a page loads faster, a spinner goes away, an error message changes), the PR must show that too. A waterfall or table explains why, but not what the user gets. Show one user action at both layers: the UI GIF (the spinner lasts 1.4 s on main and 0.2 s here) above the trace waterfall of the request that click fired, with one caption tying them together. The full catalog with data-collection recipes is in references/backend.md.
Verification that reads as evidence
When you're describing someone else's PR, keep what you ran apart from what the author reported ("cargo test --workspace passed (author's run); I re-ran the three new tests on b1039b3d").
Write results, not claims: "1616 passed, 0 failed, 45 ignored" rather than "tests pass". Name any flaky test and say how you checked it. For visual changes, the strongest proof is a byte-identical comparison against main for everything that shouldn't change (pixel fixture dumps compared with cmp), plus an exact count of the pixels that did change and why. For performance, give the baseline, the branch result, the noise level, and what the machine was doing at the time ("other agents were building during the main run"). Never claim a result you didn't see.
Delegating PRs to other agents
If you're orchestrating agents that each open a PR, give every one of them this skill, a brief with the repo's capture recipe, and the specific shots its PR must contain. Ask for the shots by name ("a before/after of two panes in dark and light, a 4x crop on an inverse cell, a GIF of focus moving, a slowed strip of the transition"). references/examples.md ends with the deliverables block from the brief that produced the reference PRs.
Reference files
| File | Read it when |
references/capture.md | Capturing stills or motion on web/Next.js, iOS, Android, React Native/Flutter, desktop, custom renderers, CLI |
references/fixtures.md | Building the synthetic scene or data the shots are taken of |
references/media.md | Choosing formats, labels, sizes, GIF budgets, and the composing recipes |
references/edge-cases.md | Listing what else the change could break: blast radius, mid-session transitions, frontend and backend input checklists |
references/backend.md | The change is server-side, data, infra, or full-stack |
references/hosting.md | Getting images into the PR body: gh --attach (like pasting), what it rewrites, fallbacks, video |
references/examples.md | Seeing the reference PRs dissected, a rewritten body in the target shape, and a delegation brief |
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

