Update Documentation
Scan project documentation and comments for references that no longer match the code, and fix them.
Context
- Repo root: !
git rev-parse --show-toplevel 2>/dev/null || pwd - Uncommitted changes: !
git status --short - Diff summary: !
git diff --stat $(git rev-parse -q --verify HEAD || echo 4b825dc642cb6eb9a060e54bf8d69288fbee4904) - Full diff: !
git diff $(git rev-parse -q --verify HEAD || echo 4b825dc642cb6eb9a060e54bf8d69288fbee4904) - GH Pages index present: !
R=$(git rev-parse --show-toplevel 2>/dev/null || pwd) && test -f "$R/docs/index.md" && echo yes || echo no - Doc image files: !
git ls-files --full-name -co --exclude-standard -- ':/docs/*.png' ':/docs/*.jpg' ':/docs/*.jpeg' ':/docs/*.gif' ':/docs/*.webp' | grep . || echo NONE - Screenshot manifest: !
R=$(git rev-parse --show-toplevel 2>/dev/null || pwd) && cat "$R/docs/screenshots/screenshots.json" 2>/dev/null || echo MISSING
Working directory
All file paths below (README.md, docs/, docs/pages/, docs/index.md, docs/screenshots/, CLAUDE.md) are relative to Repo root from Context. The current working directory may be a subdirectory (e.g. src-tauri/, frontend/), so always prefix the Repo root value when calling Read/Edit/Write/Grep/Glob. Bare paths are cwd-relative and will silently miss files that live at the actual root.
Process
Read
README.md(at the repo root) and fix any references to changed paths, APIs, or behaviorRead all files in
docs/pages/(if the folder exists) and check for both staleness and gaps:- Rewrite any sections that no longer match the code — removed features, changed message protocols, new data flows, renamed concepts
- Check whether features added or significantly changed in the current diff are missing from the relevant docs page. A new user-visible capability, config option, or behavioral change should appear in the page that covers its area. Propose additions and wait for approval
- Update the features section. When the diff adds a new user-facing feature or changes an existing one, the curated feature listings must reflect it — the dedicated features page (e.g.
docs/pages/features.md), any enumerated feature list indocs/index.md, and the README's feature list. Add a new entry for a new feature; revise the existing entry for a changed one. A feature that exists in code but is absent from (or stale in) these listings is a documentation gap even when every other page is accurate. Propose the additions/edits and wait for approval - Check embedded diagrams (
```mermaidblocks, ASCII flowcharts/trees, and structural tables) that depict architecture, data flow, state machines, or file layout. When code changes — or another doc you just edited — alters the structure a diagram illustrates (a renamed/removed module, a new component, a changed path or edge), update the diagram itself, not just the surrounding prose. A stale node, missing arrow, or wrong label in a diagram is as misleading as stale text. - Check pages that moved in the nav hierarchy (e.g. a former top-level page now nested as a subpage). A relocated page often (a) duplicates content that now belongs to a sibling page — trim it to a pointer so each page owns one concern; and (b) keeps heading levels from its old position (a former subsection's
###where siblings use##). Also re-verify its relative links resolve from the new location.
Align README with the GH Pages index — only if GH Pages index present is
yes. ReadREADME.mdanddocs/index.mdtogether and reconcile them so they describe the same product at the same point in time:- Tagline / one-line description must match (ignoring italics and minor punctuation).
- The set of user-facing features / supported sites / supported games listed in each must match exactly — no feature appears in one but not the other.
- Per-feature blurbs in the README must match the intro paragraph of the corresponding
docs/index.mdsection (same facts, same scope claims). Wording may differ slightly; facts must not. - Install link, beta / access notices, and status blurbs must match.
- If the README contains per-feature blurbs, each feature link must point to
https://<org>.github.io/<repo>/pages/<feature>(or/pages/<product>in the monorepo variant) and the correspondingdocs/pages/<feature>.mdfile must exist. - The footer "See full project documentation at …" block in the README must list every page that exists under
docs/pages/(user-facing pages + Developer guide); no page may be listed that doesn't exist, and no existing user-facing page may be missing. - When in doubt about which side is correct, treat
docs/index.md+docs/pages/<feature>.mdas the source of truth and update the README to match.
Check documentation screenshots. A screenshot is documentation that goes stale invisibly — the prose beside it stays true while the pixels stop being. Run the staleness checks whenever Doc image files is not
NONE; when it isNONE, say so rather than staying silent, and go straight to the missing-shot pass below — a project with no images at all is the one most likely to want its first. Report the counts on every run, including a clean one.- Start from the manifest, and leave it more complete than you found it. Screenshot manifest in Context is
docs/screenshots/screenshots.json— one entry per screenshot or per series taken together, recording what the frame shows, how to reproduce it, and whether this skill may replace it (auto/confirm/never). Seereferences/screenshot-manifest.mdfor the schema, the policy semantics and the upkeep rules; read it before writing to the file. When Context reportsMISSING, build the manifest during this run rather than working without one — every fact the steps below establish belongs in it, so the next run does not re-derive them. - Ask about every undecided screenshot, once. A file in Doc image files with no manifest entry, or an entry carrying no
policy, has never been ruled on. Ask the user which policy it should have —auto(replace it without asking),confirm(propose, replace on approval), ornever(report only; they take the picture) — and write their answer intopolicy. Ask about all of them in one exchange, not one question per image. When the question cannot be asked, write the entry with everything else you know and nopolicykey at all, then name those entries in the report so the next interactive run settles them. Never infer a policy from how a similar image was treated elsewhere. - Reconcile the manifest against the files too. An entry whose files have all vanished is orphaned metadata — report it, and remove the entry only once the user confirms the image was dropped on purpose. An entry whose
fileslist has grown stale (a renamed image) is a rename to fix, not a new screenshot to register. - Reconcile the files against the references. List the referencing markup with
git grep --full-name -InoE '\]\([^)]+\.(png|jpg|jpeg|gif|webp)\)|src=.[^ >]+\.(png|jpg|jpeg|gif|webp)' -- ':/docs' ':/README.md' ':!/docs/plans'and resolve each path relative to the file citing it, skippinghttpanddata:URLs. A reference with no file is DANGLING — a broken image on the published site. A file no reference resolves to is an orphan candidate: confirm withgit grep -F <basename> -- '*.md' '*.html' '*.json' '*.xml' ':!/docs/plans' ':!/CHANGELOG.md', which catches thehref=andcontent=forms the embed pattern misses (favicons,og:imagecards, store manifests). A prose mention is not a reference — read the matching line before it clears anything. Those two exclusions matter: a plan doc or changelog recording that an image was dropped names the file, and grepping it unexcluded clears every orphan it names, turning the whole check into a silent pass. Before reporting that nothing is wrong, confirm the scan finds a reference you already know exists — a missed embed form returns a comfortable empty result forever. - Classify a confirmed orphan before proposing anything about it. No output from
git log --oneline -S '<basename>' --all -- '*.md'means it was never referenced in any revision — wired up wrong when it was added. Output means it lost a reference it once had, which is usually a deliberate removal recorded in a commit or a plan doc. Also look for a derived twin by stem before calling anything dead: the master underdocs/is often the source of a converted copy that a published site tree still serves, so an image orphaned in the docs can be live on the website. Report either kind; delete neither without asking. - Examine every shot that has never been examined; scope the rest to the diff. An entry with no
verifiedAtis opened and checked whatever the diff says, and so is any screenshot with no entry — otherwise a shot that went stale in an earlier commit is never revisited, because its backing source is not in today's diff, and it stays wrong indefinitely while the prose beside it keeps reading true. Record the current short sha inverifiedAtonce a shot has been examined, stale or not. For shots that already carry one, map each page embedding a screenshot to its backing source by grepping the page's bolded and code-formatted terms over the source tree — filename tokens and co-change history rank candidates, they are not evidence — and check it when those files appear in Diff summary. In either case, once a shot is being checked, Read the screenshots that page embeds and check the code two ways: every literal visible in the shot still exists in source, and every set the source enumerates — menu items, tabs, status labels — appears in the shot in full. Scope the grep with':!*.md' ':!docs' ':!CHANGELOG.md'; unscoped, it hits the changelog entry describing the removal and reports a false negative. A missing literal, or a set the shot shows only part of, is STALE — name the commit withgit log -S, ordering same-day commits bygit merge-base --is-ancestor, never by date. - An impressionistic frame is held to what it argues, not to its literals. A hero collage, a store marquee, an angled or overlapping composite exists to show that something is broad — several surfaces, several games, a lot of product — and is not read for a particular toggle or label. Literal matching flags such a frame on almost every release, and a verdict that fires every time trains the reader to skip it. Mark it
"precision": "impressionistic"in its manifest entry and judge it by its claim instead: it goes stale when the claim does — a game it advertises is dropped, a whole surface it should show is missing, it no longer reads as broad — never because a control inside one of its panels gained an option. Still open it, still recordverifiedAt, and say on the contact sheet which standard it was judged by. The default is literal; write the key only when the user tells you the frame is impressionistic, since it lowers what every future run will catch. - Only strings this repo renders are evidence. A frame containing third-party UI — a mail client, a game table, the IDE's own menus, whatever runs inside a terminal pane — holds stable text that will never appear in this source, and reading its absence as proof produces a confident false positive on exactly the projects whose screenshots must not be re-shot. Where the frame is not wholly this project's UI, report
NOT CHECKED (third-party UI in frame)and prove nothing. Making the check work there takes one hand-written line in that project'sCLAUDE.mdnaming the source trees that own the strings — propose it, never guess it. - No verdict from a date. File mtime is the checkout time and is identical across the whole tree; commit age says only that the codebase moved on. Neither is evidence of anything about an image.
- When a shot is stale, sweep its neighbourhood before reporting: its alt text and caption, the prose describing it, every other page embedding the same file, sibling shots of the same screen, and any second copy under a published site tree — a
site/or equivalent directory with its own derived images and its own<figcaption>text, which goes stale independently of the image it captions. Say whether that tree was scanned. Do not compare a master's dimensions against a derived copy's; deliberate re-crops make that a permanent false positive. - Report three verdicts and the size of the haystack —
STALEwith its proof,NOT CHECKEDwith its reason, and a count of the images only inventoried. Give the reach in one sentence: which directories were scanned, which images were opened rather than listed, and that this check reads text, so it can prove a shot stale and can never prove one current. Say what the manifest gained this run: entries added, policies answered, entries still carrying nopolicy, and any image replaced underauto— a capture that happened without a question must be the loudest line in the report, not a silent one. - Look for the shot that is missing, not only the one that is wrong. The checks above all start from an image that exists, so a page that should carry one and never has is invisible to every one of them — the failure mode is a feature described in three paragraphs of prose that a picture would have settled in a glance. Sweep the user-facing pages for sections that describe something with a visual result — a window, a panel, a menu, a chart, a badge, a state the UI shows — and carry no image. Rank the gaps by how much the prose is straining: text naming a layout ("the header tracks…", "a small badge carrying…"), enumerating what is on screen, or describing where to click is doing a picture's work. A section documenting behaviour with no visual surface (a config field, a protocol, a timing rule) is not a gap — do not manufacture one. Sections whose sibling pages already carry a shot rank higher, since the inconsistency is itself confusing. Propose at most the two or three strongest per run, each as: the page and section, what the shot would show, and why the prose alone is not enough. Then ask whether to capture it. This is the one place in this skill that may drive the product, so it is gated on an explicit yes every time — never bundle it into another approval, and take silence as no. Number these in their own series — G1, G2, … — so a reply can take some and drop others without colliding with the frame numbers below, and carry the user's triage forward so a later run does not re-raise what they already declined.
- Replacing a stale shot is governed by its entry's
policy. Detection above runs for every screenshot; only replacement is gated.never— report it and stop. The user takes the picture.confirm— say what changed and what the replacement would show, then wait. Replace on approval.auto— replace it, then report what was replaced and why. No question first.- An absent
policy, and a screenshot with no entry at all, both behave asconfirm— which is why undecided is safe to leave lying around: it stops to ask, so nothing is ever captured on silence. Settle it by asking, not by acting. Undernever, and under an absentpolicy, a replacement is not offered: a stale shot means the UI moved, and which of the several states it now has is worth documenting is the user's call, not something to infer. But when they ask for one and name the state, that reason is spent — an instruction like "recapture it, and make sure nothing sensitive is in frame" has already answered the only question the restriction was protecting, and refusing at that point is deferring to a rule instead of to the person the rule exists for. Capture it under every honesty rule below, and show them the result before it replaces anything.
- Every capture runs a script, including the first one. Wherever a capture is permitted —
confirmafter approval,auto, a named-state request, or an approved gap — run the entry'scapture.command: a recorded script reruns identically, lands in the diff, and does not depend on the machine's window size, theme or which apps are open. When no command exists yet, write the script rather than driving the app ad hoc: turncapture.stepsintodocs/screenshots/capture/<id>.sh, run it, iterate on the script until the frame is right, then record it incapture.command. That directory is the only capture surface this skill drives directly, which is deliberate — it makes the second capture free and the diff reviewable, and it stops a one-off sequence of clicks from becoming the only record of how a published image was made. The script is what reruns; the staging and the shutter below are what it calls. A capture script builds against the current working tree, never an installed release, or the shot documents a build nobody has. - If the capture runs off fixture data, build the fixture the frame needs. A script that renders from whatever fixture was already lying around produces a correct picture of the wrong moment — a history with no compound action, a deck with nothing identified — and quietly stops illustrating the sentence beside it. That is a fixture problem, not a reason to accept a thinner shot or to go back to shooting by hand. Find the real session the original came from (its distinctive content is the search key — a card name or a label visible in the old frame will often identify it outright), commit the smallest artifact that reproduces it, and add whatever flag selects the moment. Prefer a processed intermediate to raw capture data when the renderer accepts one — an order of magnitude smaller, and the parser that produces it has its own tests. Keep this data out of the product: a fixture beside the capture scripts is documentation support, and it must never become a seed-data hook in the shipped code.
- Match the device pixel ratio of the shots you are not replacing. A hand-taken screenshot carries the DPR of the display it came from — commonly 1.5 or 2 on a scaled monitor — while a headless capture defaults to 1. The replacement is then correct and renders about a third smaller, which no text check can see and which shows up as two apparent zoom levels on one page, since the un-replaced frames beside it are still at the old ratio. Measure it rather than guessing: a repeating feature in the old shot (a card, a row, a cell) against the same feature rendered at 1 gives the ratio directly, and the numbers land on a clean value. Match the framing width the same way — the old shot's pixel width divided by that ratio is the CSS width it was taken at, and a wider viewport silently shrinks everything laid out to fill it.
- Writing that script: staging, then the picture. Two separate problems, and they are worth keeping apart: staging the thing (getting the right window open, showing the right content) and taking the picture.
Staging — climb down from the top, never start at the bottom. In order: (1) the app's own interface — a URL scheme, CLI, or local HTTP route — which needs no OS permission, says what it means, and does not break when the UI moves; (2) App Intents /
shortcuts; (3) Apple Events against the app's scripting dictionary, whose Automation grant is scoped per source→target pair; (4) the Accessibility API, semantic but a system-wide grant; (5) synthesized clicks at coordinates. Rung 5 needs the same broad grant as 4 while being strictly more fragile, and a helper that injects input is a general "control this machine" capability — expect it to be refused, and do not treat that as an obstacle to route around. For an app the project owns, the answer is usually to give it rung 1 rather than to puppet its UI — that is a legitimate control surface, and distinct from the fabrication the rules below forbid: it commands what is shown, never what is true. Taking the picture needs Screen Recording on macOS, and where that grant goes matters. Not to the agent's own binary: it is version-named, so the grant goes stale on every update, and it would hand whole-display access to every session forever. A small purpose-built helper at a fixed path with a stable signature is granted once and scoped to the one job —~/.claude/skills/documentation/scripts/holdsdocshot.swift(capture) andbuild-docshot.sh, which installs it asDocShot.appand refuses to clobber an existing install, since rebuilding resets the grant. Two traps that cost real time: TCC attributes the request to the responsible process, so the helper must be launched through LaunchServices (open -a … --args) or the grant is ignored and it must therefore return results in a file rather than on stdout; and a denial is silent — window ids, owners and bounds still populate while every title comes back empty, so probe for that (--check) rather than discovering it as a black PNG. Then the honesty rules that make the result worth committing:- A screenshot of an empty app is worse than no screenshot. Most UI worth documenting only looks like itself with real content in it — a session list with sessions, a chart with history, an inbox with mail. If the state cannot be reached legitimately, say so and hand the capture back to the user rather than shipping a picture of an empty shell.
- Never fabricate the state. No demo mode, no seed data, no fixture wired into the product to make the picture easier — the out-of-scope rule below is absolute and no approval lifts it. Real state or the user's own capture.
- Crop to the subject. A whole-desktop grab of a small widget documents the wallpaper. Capture the window, not the screen.
- Frame with an even margin, and let the pixels decide it. A subject that stretches to its container, a page shorter than its own bottom padding, or a grid whose widest row stops short each leave a band of background on one side and none on the other — and choosing a viewport width only frames a subject that happens to fill it. Trim to the pixels actually drawn and pad back out to a uniform margin, so the frame is even whatever the subject did. Pad outward rather than clipping a wider region: on a stacked layout the neighbour sits a few pixels away, so a generous clip frames the subject with someone else's content. Do not do this to a shot over a textured or photographic background — the trim keys off a flat background colour, finds "content" everywhere, and pads a solid-colour border around the outside; a hand-cropped shot of a real page is usually already tight, so leave it.
- Capture the background out, don't crop it out. Anything that isn't a plain rectangle — rounded window corners, a drop shadow, a translucent overlay — has boundary pixels that are a blend of the subject and whatever was behind it, so no crop separates them: trimming either leaves a grey fringe or eats content, and for a genuinely translucent window there is nothing to trim at all. Photograph the subject twice over known backdrops instead and solve
O = C·a + B·(1−a)per pixel for its real colour and coverage, writing a PNG with an alpha channel. The shot then composites correctly on a light page or a dark one, which a baked-in background never does. Treat a stray dark or light fringe at a corner as this problem, not as a crop that needs another pixel or two — a rectangular trim is the diagnosis (corners that stay the backdrop's colour prove the subject is round), never the remedy. Do not re-derive this.~/.claude/learnings/windows-window-capture.mdcarries the solved form (a = 1 − (O_w − O_k)/255,C = O_k / a), how to place the backdrop, and two things the formula alone will not tell you: the recovered colour is also the diagnosis — a fringe coming back as the subject's own colour at rising alpha is background and the alpha removes it, while one coming back dark at ~50% coverage is the window's own chrome, which no keying will touch and which must instead be inset past — and that verifying with a threshold tuned to the previous defect sails straight past the next one. A backdrop need not be a window behind a window: for anything rendered in a page — a web app, a themeable UI — the two known backdrops are two values of its own background, which is usually one line of CSS and easier than the desktop case the learning was written for. - A known shape needs only one capture. The two-backdrop solve exists because both the colour and the coverage are unknown at a boundary pixel. When the subject is a shape you can describe — a rounded rectangle, a panel, a card — its geometry supplies the coverage, and a single capture is enough: build the mask analytically, supersampled, and apply it. Two details decide whether it looks right. Inset one pixel: a mask alone leaves a fringe, because the pixels it half-covers really are half backdrop, and dropping that ring takes the fringe with it at the cost of a corner a pixel tighter — invisible at viewing size. Do not un-blend an inset edge: with an inset the boundary sits inside the subject, so a partial pixel is pure subject being antialiased, and subtracting a backdrop that is not in it over-brightens toward white (seen as a 7%-alpha white fringe that passed every numeric check). Un-blending is correct only at zero inset. Masking by shape also sidesteps a backdrop too close to the subject to colour-key — in one case 28 levels of blue apart. Two consequences: the border that follows must trace the shape, since a keyed image has no rectangular edge for a canvas-wide box to sit on; and estimate the subject's own luminance from a median over a grid, because a single sample lands on text as readily as on fill and will pick a dark outline for a dark panel.
- Give a screenshot an edge when its own capture has none. A shot whose border colour matches the page it lands on has no visible boundary — a dark app on a dark page, a light app on a light one — so the reader cannot see where the picture stops. macOS draws a decorated window a light hairline and transparent rounded corners for free, which is why a shot of one already reads correctly; an undecorated window has neither, and a region crop never does. Prefer a stylesheet rule where the surface has one —
~/.claude/skills/github-pages/SKILL.mdgives the docs-site form — and bake the edge into the pixels only where CSS cannot reach, such as a README rendered on github.com, whose sanitizer stripsstyle. When baking it, stroke a mid grey rather than a light hairline (scripts/docborder.swift, built to a plain binary — it needs no permission, and folding it into the capture helper would reset that helper's TCC grant): a light grey vanishes on a light page and a dark one on a dark page, and a baked border cannot follow a theme the way a stylesheet can. This is framing, not retouching: it adds the edge the OS would have drawn and changes no pixel of the content. Check before assuming — sample a corner and a top-edge pixel;(0,0,0,0)corners with a light edge mean the window already brought its own. - Read back what you captured before wiring it in, and run it through the staleness check above as if it were an inherited file: every literal in the frame must exist in source. A shot taken from a dirty working tree can already be wrong on the day it is committed.
- Wire it in per the path table the
github-pagesskill owns, write alt text that says what the image shows rather than repeating the caption, and report the new file as part of the change set so it is committed with the prose it illustrates.
- Give a screenshot an edge when its own capture has none. A shot whose border colour matches the page it lands on has no visible boundary — a dark app on a dark page, a light app on a light one — so the reader cannot see where the picture stops. macOS draws a decorated window a light hairline and transparent rounded corners for free, which is why a shot of one already reads correctly; an undecorated window has neither, and a region crop never does. Prefer a stylesheet rule where the surface has one —
- Filing any replacement, whoever produced it. A user-supplied replacement — with nothing attached, the newest PNG on their Desktop — is filed the same way as a captured one. Compare its dimensions against the file it replaces with
file <path>and raise a mismatch before going further; copy it into place; then run the neighbourhood sweep above so the caption, alt text and prose move with it. Do not overwrite the committed image until the replacement is in hand and accepted. - Write every artifact this run produces into the repo's own gitignored
tmp/, never$TEMP. The file tools refuse reads outside the working directories, so a sheet written to$TEMPis one this skill can build and hand over but never open again to check — which breaks the rule that sends it to a browser in the first place. A project-localtmp/needs no read grant, behaves identically on the Windows and macOS machines, and cannot collide with another repo's run; add/tmp/to the project's.gitignoreon first use, anchored with the leading slash so it matches only the repo root. Keep a descriptive<artifact>-<date>name for the sheet and for the directory of saved originals — the repo name that used to be in it is now carried by the directory. Apply the same rule to anything else this skill writes outside the change set. - Every replacement is reversible. Copy the outgoing file to
tmp/screenshot-originals-<date>/before overwriting it —autoreplaces without asking, which makes the undo path the only thing standing between an unattended run and a lost image. Do not delete those copies when the user accepts: the contact sheet's before/after pairs point at them, so discarding them blanks half of every pair and leaves an artifact that can no longer show what it argues. For a tracked image the copy is belt-and-braces anyway —git checkout -- <path>restores the committed version, andgit show HEAD:<path>recovers it for the sheet — so the copy exists for the untracked case and for as long as the sheet is worth reading. - The contact sheet is how this check talks to the user about pictures, and it comes before the change more often than after. Any run with something visual to settle writes one HTML page outside the repo and links it — a stale frame, a proposed replacement, a verdict worth a second pair of eyes. Under
confirm,never, and an absent policy the sheet is the proposal, so it is written before any capture: "say what changed and what the replacement would show" is a claim about pictures, and no amount of prose puts the picture in front of the person deciding. Onlyautowrites it afterwards, as evidence of a swap already made — permission to skip the question is not permission to skip the evidence, and an image swapped unattended is exactly the one whose before and after must be easy to compare. The trigger is reporting about pictures, not finishing a run — if a reply says anything about what an image shows, changed, gained or lost, it is accompanied by a sheet, whatever produced the image and whichever skill is loaded. Skip it only when the run touched no images at all. The rationalisations for skipping are always the same and always wrong: the change is one pixel (an argument for magnified crops, not against the sheet), it is uniform across many images (uniformity is itself a claim about images), they have seen a representative example inline (a sample the reader did not choose, so they cannot see the one that went wrong), and this was not a documentation run (the pictures do not know that). Every time this was skipped the thing missed was visual and uncatchable in prose. Build the sheet before writing the summary, and let the summary point at it rather than repeat it. - What goes on it. Every screenshot, not only the ones in question — anything replaced as a before/after pair with both sets of dimensions, everything else as the picture currently committed. "Unchanged" and "not implicated" are claims about an image, so a page that argues them in prose while showing only the ones that changed asks the reader to take the quiet ones on trust; it is also where a wrong verdict gets caught, since this check reads text and cannot see layout, a cropped edge or a control that moved. Each frame carries its staleness proof, and each one awaiting a decision carries what a re-capture would need — the session, the app state, the preconditions — so the cost of the shot sits beside the case for it. Build it from
references/contact-sheet.html. Copy that file and fill its slots — do not hand-roll the HTML from this description. The prose here says what goes on the sheet; the template is what makes two runs, in two repos, by two agents, produce the same page. Skipping it silently drops the parts described nowhere else: the#introsection with its lede and the summary table of every frame, the legend,W × H → W × Hin each meta line, the 6× nearest-neighbour corner crops that carry pixel-level proof, the#themetoggle that tests a contrast claim against both themes, and ← / → stepping.references/screenshot-manifest.mdexplains why each part is there. Open the finished page in a browser before handing it over, which is the check that catches what grepping the HTML cannot. Do not substitute reading images into the transcript: they arrive in the order they were read, split by tool output, and six pictures from a three-image run cannot be paired up by eye. Open it withplannotator annotate <report>.html(the CLI takes.htmlas well as.md), so the user can answer from the page and annotate it in place. Embed each image as a base64data:URI, never a path: the page is served over http, and a browser refuses to load afile://image from a served page, so a relative or absolutesrcrenders as a broken icon and the report proves nothing. Scale for display with CSS (max-width) — never by touching the file. A clean pass stays a one-line text summary: do not open a browser to say nothing is wrong. - Number the frames, and make them one click apart. Every entry gets a number matching its manifest order, shown in its heading and used in the chat write-up too, so "re-shoot 2 and 6" is unambiguous where "the help one and the crew suits one" is not. The sheet carries a fixed list of those numbered entries down the left, outside the content column, each switching to its frame on a single click; a page that can only be scrolled makes the reader hunt for the shot they were just told about. Keep an All frames view alongside it for reading straight down, since the argument for showing every frame is that they get compared.
- Then ask, with the sheet open. The page states the case; it does not collect the decision. Ask once, covering every screenshot in question, and let the user settle each on its own — a run that proposed three replacements may have got two right. Before a capture the question is which frames to shoot, and whether the framing described is the one they want. After one it is keep, revert, or re-capture: revert copies the saved original back; re-capture means the framing was wrong, so change
docs/screenshots/capture/<id>.shand run it again — never re-shoot by hand around a script that produced the wrong thing, because the next run would reproduce that same wrong thing. - Do not restate layout or markup rules here —
~/.claude/skills/github-pages/SKILL.mdowns thedocs/screenshots/location, the per-page relative-path table, and the hero-versus-inline image markup. Consult it when a screenshot has to be added, moved, or re-pathed.
- Start from the manifest, and leave it more complete than you found it. Screenshot manifest in Context is
Read
CLAUDE.md(project-local.claude/CLAUDE.mdif it exists, otherwise repo root) and fix any stale file descriptionsCheck comments and docstrings in modified source files (use Uncommitted changes and Full diff to identify them) that reference changed behavior
Update dimensioned drafts — only if the repo keeps drafts (Glob
**/dimensioned_drafts/*.pyoutside ignored dirs; skip this step when nothing matches). Drafts are documentation of model geometry: when a model source file changed in the diff, find the draft scripts that document it (match by model name/directory and by constants mirrored from the model's dimensions class) and check every drawn value — dimensions, profile vertices, removed/added features, not just labels. Update the draft script to the current model, re-run it to regenerate the SVG, and include both files in the change set. A draft documenting a feature the model no longer has is stale documentation just like prose.Suggest improvements — if documentation would benefit from a new file or reorganization, suggest it to the user and wait for approval before proceeding
Report what was updated. If nothing was stale, say so. Call out README ↔
docs/index.mdmismatches explicitly, even when fixed. When step 4 opened an HTML report, say so and do not restate its findings in the chat — repeating them splits one decision across two surfaces, and the annotations come back from the page.
Out of scope
- Do NOT touch code logic — only comments, docstrings, and doc files
- Do NOT create new documentation files or restructure existing ones without explicit approval
- Do NOT capture, crop, resize, recompress or retouch an image whose manifest entry says
never— report those and let the user decide. Capture without asking is permitted only underauto; every other state, including an absentpolicy, requires their approval for that specific replacement first. Opening the step-4 HTML report is not covered by this: it renders findings to look at rather than producing an image, and alters nothing on disk - Do NOT capture a new shot for a documented gap without the explicit approval step 4 requires — never bundled into another yes, never inferred from enthusiasm about the gap itself
- Do NOT delete an image file, and do NOT remove a manifest entry, without explicit confirmation
- Do NOT write a
policythe user did not give you, and do NOT raise one (never→confirm→auto) on their behalf — it widens what future runs may do unasked, so the key is theirs alone. Leaving it out is always available and always safe. Every other field is this skill's to maintain - Do NOT add a screenshot flag, demo mode, or seed-data hook to the product to make a capture easier. This holds even with a capture approved: permission to take the picture is not permission to change the product so the picture looks better. If the state cannot be reached legitimately, the shot is the user's to take