Describe changes
Ask what a pull request did and you usually get its title back, reworded. The
title is the author's intent at the moment they opened it, which is frequently not
what the diff ended up doing. This skill produces a description that is checkable:
every claim in it points at a hunk.
It writes three registers of the same change — one line, one paragraph, one page —
so the consumer picks the length rather than truncating prose written for another
purpose. And it classifies, from a fixed set, so the descriptions sort.
It composes with two companions in the same pack: release-ledger orchestrates
a since-you-were-away ledger and consumes these descriptions as its entries, and
github-webhooks captures the merge events that feed it. Install either with
npx skills add crissmoldovan/agent-skills.
When to Use
- A commit, pull request, merge, or release range needs a description for people
who did not write it.
- A ledger, changelog, or release note needs a per-change entry with a
classification.
- A ticket needs a resolution note that states what actually shipped.
- Someone asks "what changed between these two tags" and needs more than a
commit list.
Do not use it to review a change — it describes what happened, it does not judge
whether it should have. Do not use it to write a commit message before the work
is done; it reads a completed diff. Do not use it as a summariser for a body of
text with no diff behind it; the entire value here is that claims are anchored,
and with nothing to anchor to you have a paraphrase.
Prerequisites
- A named operation. One commit, one pull request, one merge, or one range
A..B. Ambiguity here produces a description of the wrong thing.
Complete when: you can state the operation as an identifier a tool accepts.
- Read access to the diff.
git show, git diff A..B, or the forge's diff
endpoint.
Complete when: you have the diff, or have established that you cannot get
it and will therefore write only the short and medium registers.
- The surrounding context, where it exists. The pull request body, linked
tickets or support requests, the reporter, the review outcome.
Complete when: each of those is either in hand or recorded as absent.
Absent is a normal answer.
- A decided output shape. Single digest for the whole operation, or an
itemized array plus a summary. Ask; do not choose silently.
Complete when: the user has answered, or the caller passed the shape in.
Procedure
- Gather the evidence, in this order. Diff first — file list, then hunks,
then the test changes. Then the pull request title and body. Then linked
tickets and the reporter. Then the review and check outcomes. Reading the
description before the diff anchors you to the author's framing, which is the
thing you are here to check.
- Establish what the change is, mechanically. Which files, how many lines,
which subsystems, whether tests moved, whether a schema or an interface
changed, whether anything was deleted. This paragraph of facts is the input to
classification; do it before choosing a kind, not to justify one.
- Classify into exactly one kind from
feature, bug_fix, improvement,
security, ops, docs, breaking. The rubric and its edge cases —
a bug fix that adds a flag, a pure performance change, a revert, a dependency
bump that fixes a vulnerability — are in
the classification guide. One kind. If
two genuinely apply, that is a signal the operation should be itemized rather
than digested.
- Write the three registers.
- short — one line, at most 140 characters, no markdown syntax beyond
inline code, no trailing period needed. It answers "what changed" for
someone scanning a list.
- medium — one paragraph. What changed, who it affects, and what they will
notice. No file paths, no function names; this register is for users.
- detail — a full markdown page: what changed, why, and the specific
evidence. File paths with line ranges, hunk citations, before/after
fragments where a fragment is clearer than a sentence. This register is for
someone who will open the code.
- Anchor every claim in the detail register. A sentence that asserts
behaviour must be followed by, or adjacent to, the reference that shows it. If
you cannot point at the hunk, delete the sentence.
- Record relations, cited or omitted. Ticket, request, reporter. A relation
is included only when the evidence names it — a reference in the body, a
linked issue, a trailer. Do not infer a reporter from an assignee, or a ticket
from a branch name that resembles one, without saying that is what you did.
- Emit the agreed shape. The JSON envelope in
the output contract, single or array. Markdown
lives inside the JSON string fields; the envelope itself is data.
Honesty rules
These are not style preferences. Each one exists because the opposite behaviour
produces a description that reads well and is false.
- Never claim a change does something the diff does not show. "Improves
performance" requires either a measurement in the change or a mechanical reason
visible in the hunk. Otherwise write what the code now does and stop.
- A revert says it is a revert, names what it reverts, and does not describe
the reverted change's benefits in the present tense.
- A partial change is described as partial. Scaffolding behind a disabled flag
is not a shipped feature; say the flag is off.
- Deletions are changes. A removed endpoint, option, or column belongs in the
description even when nothing was added.
- Absent evidence is stated, not filled. No reporter is
null, not a guess
and not an empty string that renders as a dangling label.
- The title is evidence, not a conclusion. When the diff contradicts it,
describe the diff and note the discrepancy in the detail register.
Usage Examples
Describe this merge for a release ledger. Read the diff first, classify it from
the fixed kind set, and give me short, medium, and detailed registers with the
detail register citing file and line ranges. Return the single-digest JSON shape.
Describe the range v2.3.0..v2.4.0. Ask me first whether I want one digest or an
itemized array — I think itemized, with a generic summary on top. Anything you
cannot anchor in a diff, leave out.
This PR says it is a performance fix. Check that against the diff before you
classify it, and if the diff does not show a performance change, say so in the
detail register and classify what it actually is.
Pitfalls
- Describing the title. The most common failure, and the hardest to see,
because the output is fluent and plausible. Read the diff first, every time.
- Merging two changes into one description. A pull request that fixes a bug
and refactors a module has two facts in it. One kind and one description will
misrepresent one of them. Offer the itemized shape.
- Detail that is a file list. "Changed
router.ts, handler.ts, and
types.ts" is not detail; it is the diff's table of contents. Detail says what
the change to each file does.
- Line references that rot immediately. Anchor to the post-change line
numbers and pair them with the commit SHA, so the reference stays resolvable
after the next merge.
- A medium register full of identifiers. It is the register users read.
Function names there mean it was written for the wrong audience.
- Claiming a security fix without evidence. Only classify
security when the
change closes an actual exposure the diff shows, or an advisory names it.
Calling routine hardening a security fix devalues the label the one time it
matters.
- Silent inference of relations. A branch named
fix/1234 is not a citation.
Either the evidence names the ticket or the relation is null.
- Describing generated or vendored churn. A lockfile update and a regenerated
client are volume, not content. Say what the regeneration was for.
- Losing the
breaking signal in prose. If callers must change, breaking
is the kind and the first line of the detail register says what to change.
Verification
Deeper reading
- Output contract: the JSON envelope, both
shapes, field-by-field rules, and complete worked examples.
- Classification guide: the seven kinds
with their boundaries, a decision order that resolves overlaps, and the edge
cases that get classified wrong most often.
1---2name: describe-changes3description: Document what a change actually did: analyse a commit, PR, or merge, classify it, and write short, medium, and detailed descriptions anchored to the diff.4license: MIT5---67# Describe changes89Ask what a pull request did and you usually get its title back, reworded. The10title is the author's intent at the moment they opened it, which is frequently not11what the diff ended up doing. This skill produces a description that is checkable:12every claim in it points at a hunk.1314It writes three registers of the same change — one line, one paragraph, one page —15so the consumer picks the length rather than truncating prose written for another16purpose. And it classifies, from a fixed set, so the descriptions sort.1718It composes with two companions in the same pack: **`release-ledger`** orchestrates19a since-you-were-away ledger and consumes these descriptions as its entries, and20**`github-webhooks`** captures the merge events that feed it. Install either with21`npx skills add crissmoldovan/agent-skills`.2223## When to Use2425- A commit, pull request, merge, or release range needs a description for people26 who did not write it.27- A ledger, changelog, or release note needs a per-change entry with a28 classification.29- A ticket needs a resolution note that states what actually shipped.30- Someone asks "what changed between these two tags" and needs more than a31 commit list.3233Do not use it to review a change — it describes what happened, it does not judge34whether it should have. Do not use it to write a commit message before the work35is done; it reads a completed diff. Do not use it as a summariser for a body of36text with no diff behind it; the entire value here is that claims are anchored,37and with nothing to anchor to you have a paraphrase.3839## Prerequisites40411. **A named operation.** One commit, one pull request, one merge, or one range42 `A..B`. Ambiguity here produces a description of the wrong thing.43 **Complete when:** you can state the operation as an identifier a tool accepts.442. **Read access to the diff.** `git show`, `git diff A..B`, or the forge's diff45 endpoint.46 **Complete when:** you have the diff, or have established that you cannot get47 it and will therefore write only the short and medium registers.483. **The surrounding context, where it exists.** The pull request body, linked49 tickets or support requests, the reporter, the review outcome.50 **Complete when:** each of those is either in hand or recorded as absent.51 Absent is a normal answer.524. **A decided output shape.** Single digest for the whole operation, or an53 itemized array plus a summary. Ask; do not choose silently.54 **Complete when:** the user has answered, or the caller passed the shape in.5556## Procedure57581. **Gather the evidence, in this order.** Diff first — file list, then hunks,59 then the test changes. Then the pull request title and body. Then linked60 tickets and the reporter. Then the review and check outcomes. Reading the61 description before the diff anchors you to the author's framing, which is the62 thing you are here to check.632. **Establish what the change is, mechanically.** Which files, how many lines,64 which subsystems, whether tests moved, whether a schema or an interface65 changed, whether anything was deleted. This paragraph of facts is the input to66 classification; do it before choosing a kind, not to justify one.673. **Classify into exactly one kind** from `feature`, `bug_fix`, `improvement`,68 `security`, `ops`, `docs`, `breaking`. The rubric and its edge cases —69 a bug fix that adds a flag, a pure performance change, a revert, a dependency70 bump that fixes a vulnerability — are in71 [the classification guide](references/classification-guide.md). One kind. If72 two genuinely apply, that is a signal the operation should be itemized rather73 than digested.744. **Write the three registers.**75 - **short** — one line, at most 140 characters, no markdown syntax beyond76 inline code, no trailing period needed. It answers "what changed" for77 someone scanning a list.78 - **medium** — one paragraph. What changed, who it affects, and what they will79 notice. No file paths, no function names; this register is for users.80 - **detail** — a full markdown page: what changed, why, and the specific81 evidence. File paths with line ranges, hunk citations, before/after82 fragments where a fragment is clearer than a sentence. This register is for83 someone who will open the code.845. **Anchor every claim in the detail register.** A sentence that asserts85 behaviour must be followed by, or adjacent to, the reference that shows it. If86 you cannot point at the hunk, delete the sentence.876. **Record relations, cited or omitted.** Ticket, request, reporter. A relation88 is included only when the evidence names it — a reference in the body, a89 linked issue, a trailer. Do not infer a reporter from an assignee, or a ticket90 from a branch name that resembles one, without saying that is what you did.917. **Emit the agreed shape.** The JSON envelope in92 [the output contract](references/output-contract.md), single or array. Markdown93 lives inside the JSON string fields; the envelope itself is data.9495### Honesty rules9697These are not style preferences. Each one exists because the opposite behaviour98produces a description that reads well and is false.99100- **Never claim a change does something the diff does not show.** "Improves101 performance" requires either a measurement in the change or a mechanical reason102 visible in the hunk. Otherwise write what the code now does and stop.103- **A revert says it is a revert**, names what it reverts, and does not describe104 the reverted change's benefits in the present tense.105- **A partial change is described as partial.** Scaffolding behind a disabled flag106 is not a shipped feature; say the flag is off.107- **Deletions are changes.** A removed endpoint, option, or column belongs in the108 description even when nothing was added.109- **Absent evidence is stated, not filled.** No reporter is `null`, not a guess110 and not an empty string that renders as a dangling label.111- **The title is evidence, not a conclusion.** When the diff contradicts it,112 describe the diff and note the discrepancy in the detail register.113114## Usage Examples115116```text117Describe this merge for a release ledger. Read the diff first, classify it from118the fixed kind set, and give me short, medium, and detailed registers with the119detail register citing file and line ranges. Return the single-digest JSON shape.120```121122```text123Describe the range v2.3.0..v2.4.0. Ask me first whether I want one digest or an124itemized array — I think itemized, with a generic summary on top. Anything you125cannot anchor in a diff, leave out.126```127128```text129This PR says it is a performance fix. Check that against the diff before you130classify it, and if the diff does not show a performance change, say so in the131detail register and classify what it actually is.132```133134## Pitfalls135136- **Describing the title.** The most common failure, and the hardest to see,137 because the output is fluent and plausible. Read the diff first, every time.138- **Merging two changes into one description.** A pull request that fixes a bug139 and refactors a module has two facts in it. One kind and one description will140 misrepresent one of them. Offer the itemized shape.141- **Detail that is a file list.** "Changed `router.ts`, `handler.ts`, and142 `types.ts`" is not detail; it is the diff's table of contents. Detail says what143 the change to each file does.144- **Line references that rot immediately.** Anchor to the *post-change* line145 numbers and pair them with the commit SHA, so the reference stays resolvable146 after the next merge.147- **A medium register full of identifiers.** It is the register users read.148 Function names there mean it was written for the wrong audience.149- **Claiming a security fix without evidence.** Only classify `security` when the150 change closes an actual exposure the diff shows, or an advisory names it.151 Calling routine hardening a security fix devalues the label the one time it152 matters.153- **Silent inference of relations.** A branch named `fix/1234` is not a citation.154 Either the evidence names the ticket or the relation is null.155- **Describing generated or vendored churn.** A lockfile update and a regenerated156 client are volume, not content. Say what the regeneration was for.157- **Losing the `breaking` signal in prose.** If callers must change, `breaking`158 is the kind and the first line of the detail register says what to change.159160## Verification161162- [ ] The diff was read before the pull request body or title.163- [ ] Exactly one kind was assigned, from the fixed set.164- [ ] The short register is at most 140 characters and readable on its own.165- [ ] The medium register names no file paths or function names.166- [ ] Every behavioural claim in the detail register has an adjacent reference.167- [ ] References carry a path, a post-change line range, and a commit SHA.168- [ ] Relations are cited or null; none were inferred without saying so.169- [ ] A revert, a partial change, or a disabled flag is described as such.170- [ ] The output shape matches what the user chose.171- [ ] The JSON envelope validates against the output contract.172- [ ] Nothing in the description asserts a benefit the diff does not demonstrate.173174## Deeper reading175176- [Output contract](references/output-contract.md): the JSON envelope, both177 shapes, field-by-field rules, and complete worked examples.178- [Classification guide](references/classification-guide.md): the seven kinds179 with their boundaries, a decision order that resolves overlaps, and the edge180 cases that get classified wrong most often.