agentleFS
cristicretucristicretu/pr-skill/skills/visual-pr/SKILL.md

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

  • Other versions5 projects use a version of this.

    Word for word · 0

    None found.

    Edited · 0

    None found.

    Wrote their own version · 5

    1. jellydn/my-ai-tools★ 127same name
    2. mmnavarr/overseer★ 94same name
    3. thoriqakbar0/skills★ 2same name
    4. prefapp/skills★ 1same name
    5. 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
  1. Distinctive:

    Before adding a visual, write down the reviewer question it answers ("does the naive fix also work?", "does the easing overshoot?").

    line 54
  2. Very specific:

    Write every image as markdown, !alt, including inside tables.

    line 68
  3. Very specific:

    Put labels on the images too.

    line 83
  4. Distinctive:

    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 …

    line 85
  5. Distinctive:

    Do this while you build, not after.

    line 89
  6. Distinctive:

    When none of these fit, build a tiny single-file visualizer (a token bucket filling, a queue draining, replicas converging) fed by the real …

    line 128
  7. Very specific:

    When you're describing someone else's PR, keep what you ran apart from what the author reported ("cargo test --workspace passed (author's …

    line 134

“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.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

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 |
|---|---|
| ![main: …](./main-dark.png) | ![this branch: …](./branch-dark.png) |

**Moving the pointer.** <caption>
![…](./open-close.gif)          (one composited main|branch GIF)

**Slowed down.** <caption: "one tile per 8 ms; top row loses focus, bottom gains it">
![…](./transition-strip.png)

<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:

  1. 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.
  2. 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.
  3. 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.
  4. 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 ## How separates what to look at from what to read.
  • Write every image as markdown, ![alt](./file.png), including inside tables. --attach only rewrites markdown references, so <img src="./…"> won't upload in place. To pin a width, see references/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.

WeakStrong
Before and afterSame scripted fling replayed on main (top) and this branch (bottom): main holds still, then jumps a row
Checkout pageCart 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 animationOpening the sheet, then dragging it halfway and letting go. On main it snaps shut; now it settles back open
Frame stripSlowed down, one tile per 8 ms, times since the click. Watch the badge: it scales from 0.9, never from 0
Loading stateNetwork throttled to 3G: the skeleton now holds the exact layout of the loaded list, so nothing shifts when data arrives
Dark mode screenshotLook 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.

  1. 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 main and 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.
  2. 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.
  3. 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 main from 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.md has 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.
  4. 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 --probe to 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 ms labels, 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 in references/media.md.
  5. 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.
  6. Attach the media; don't commit it. This works exactly like pasting a screenshot into GitHub: gh pr create or gh pr edit with --attach ./file.png (gh ≥ 2.99) uploads each file to GitHub's attachment storage and rewrites the matching ![alt](./file.png) in 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 (in AGENTS.md, CLAUDE.md, CONTRIBUTING.md) wins. Fallbacks for when --attach isn't available, and what it does and doesn't rewrite, are in references/hosting.md.
  7. 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 shared body.md can 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 …, or gh pr edit <n> --body-file "$body" --attach … for an existing PR. Finally, run scripts/check-body.sh <n>: it fails if a ./ path wasn't uploaded or a link doesn't load. Also confirm with gh pr view <n> --json title that 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 cases table (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 diff block, 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

FileRead it when
references/capture.mdCapturing stills or motion on web/Next.js, iOS, Android, React Native/Flutter, desktop, custom renderers, CLI
references/fixtures.mdBuilding the synthetic scene or data the shots are taken of
references/media.mdChoosing formats, labels, sizes, GIF budgets, and the composing recipes
references/edge-cases.mdListing what else the change could break: blast radius, mid-session transitions, frontend and backend input checklists
references/backend.mdThe change is server-side, data, infra, or full-stack
references/hosting.mdGetting images into the PR body: gh --attach (like pasting), what it rewrites, fallbacks, video
references/examples.mdSeeing the reference PRs dissected, a rewritten body in the target shape, and a delegation brief

Download now (ZIP)

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
What did you use it for? e.g. Reviewing pull requests in a Rails app

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.