Draft or update a pull request using a disciplined PR schema (Summary, Review Claim, Review Lane, Review Unit, Safety Invariant, Slice Rationale, Non-goals, Measured, Test Plan, Revert Plan), a diff-atomicity gate, and visual-proof rules for UI-impacting changes. Always on after catstack install.sh via Cursor ~/.cursor/rules/draft-pr-precedence.mdc, Claude CLAUDE.md, Codex AGENTS.md, and slash commands /pr-skill, /draft-pr, /make-pr. Trigger when asked to make a PR, draft or update a PR body,...
4 stars
0 votes
0 copies
1 view
Added September 19, 2026
ai-agentsgobashnodetestinggit
Works with
claude code
cursor
cli
mcp
Security analysis
A92/100
mediumInstalls packages at runtime which could introduce malicious dependencies
Installs into .claude/skills of the current project.
Are you the author of Draft Pr?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/edbertchan-draft-pr)
---
name: draft-pr
description: >
Draft or update a pull request using a disciplined PR schema (Summary,
Review Claim, Review Lane, Review Unit, Safety Invariant, Slice Rationale,
Non-goals, Measured, Test Plan, Revert Plan), a diff-atomicity gate, and visual-proof
rules for UI-impacting changes. Always on after catstack install.sh via
Cursor ~/.cursor/rules/draft-pr-precedence.mdc, Claude CLAUDE.md, Codex
AGENTS.md, and slash commands /pr-skill, /draft-pr, /make-pr. Trigger when
asked to make a PR, draft or update a PR body, prepare PR text, run gh pr
create, or whenever a branch/PR change means the PR's title/body may now be
stale.
---
# draft-pr
Use this skill when the work is already done and the user wants a PR created,
updated, rewritten, split, or republished, and whenever a branch or diff
change could leave a PR's title/body out of date with the actual code.
After `./install.sh`, this is always on for Cursor, Claude Code, and Codex
(the Cursor precedence rule, Claude `CLAUDE.md`, Codex `AGENTS.md`, plus
`/pr-skill`, `/draft-pr`, and `/make-pr`). If the current repo has
`skills/make-pr/SKILL.md`, use that overlay for publication. Do not substitute
a generic `gh pr create` Summary / Test plan template.
Apply `split-scope` (this bundle's companion skill) before writing titles or
PR bodies for a stack: if one branch mixes more than one local review claim,
split the stack first.
When the current repo has `skills/make-pr/SKILL.md` (or
`engine/skills/make-pr/SKILL.md`), that overlay owns publication gates.
**Run its preflight classification before drafting any PR body** — dry-run
with commit-scoped paths is enough to learn the review unit. Draft the body
only after preflight declares one unit. Choose Mergify vs `gh` once from
that overlay before the first push, and keep that path for the slice.
## Scope & Ambiguity Confirmation
Before drafting a PR body, resolve scope and ambiguity with the user in
conversation. Do not draft through unresolved ambiguity.
Ask a concise, concrete question — instead of drafting speculatively — when,
after looking at the actual diff/changed files:
- The review lane cannot be inferred with confidence: no single lane's
compatible unit set covers the large majority of changed, non-neutral files.
- The changed files plausibly span more than one review unit under the
active `drafter.config.json` taxonomy, and grouping them isn't covered by an
explicit Boundary/Grouping rule (see `split-scope`'s Grouping Rules).
- The slice boundary is undecided: the diff could reasonably ship as one PR or
a multi-PR stack, and each framing changes what a reviewer is being asked to
approve.
- A configured pairing ban (see `drafter.config.json`) is half-triggered — one
of its two paths changed, not the other.
If none of these hold, proceed and record the resolved choice under an
`Assumptions:` note rather than asking. Do not ask about a choice that has
exactly one non-arbitrary answer.
**Safety Invariant Confirmation is a standing rule, independent of the above:**
always propose the `## Safety Invariant` text and ask the user to confirm or
correct it before finalizing a PR body — even when scope was completely
unambiguous. Mechanical slices may use a terse invariant; confirmation is
still required. Nothing in this skill or its validator enforces that a human
actually answered — this is a conversational convention you must actually
follow, not a check you can skip because the tooling would let you.
**Headless / non-interactive mode:** when invoked under a directive that
forbids clarifying questions (a benchmark prompt, an explicit non-interactive
flag), skip all asking above, resolve every open point to your best-effort
default, and record each one under `Assumptions:` instead — including the
Safety Invariant, marked as unconfirmed in the output.
## Stack ordering
Order slices so a reviewer reads the evidence before the change it justifies:
- **Repro/proof comes before the fix.** Land the repro or regression proof as
the earlier slice and the behavior fix as the later slice, so the bug is
demonstrated before the change is approved.
- Foundation (types, helpers, migrations, flags) precedes behavior; cleanup
and docs come last.
## Preferred PR schema
Default to this structure (validated by `validatePrBody()` in
`@neko-catpital-labs/drafter-core`):
```md
## Summary
The reader is a busy director who has never seen the code. Teach them from
zero.
The first paragraph says what the part is and what it does for a person.
Then one short paragraph each for the problem, the cause, and the fix.
Paragraphs, not bullets, under 30 words each (configurable via
`drafter.config.json`'s `prBody.summaryWordLimit`). Short sentences,
everyday words; explain or cut every term coined while working.
The whole Summary is 150 words or fewer. Write it with the `diu` skill:
lead with what changed for a person, then the problem and the fix, and
nothing else. Detail goes in the sections below it.
No code names in Summary or Review Claim: no backticked text, no snake_case
or camelCase words, no file paths, and no word that is the name of a changed
file or folder. Say what the part does instead. Names and output belong in
later sections: Test Plan, Revert Plan, Architecture.
Before: "When diu-stop or prove-it-ship-gate block a reply and the agent
rewrites it, the rewrite is still checked for evidence. Before, both hooks
returned on `stop_hook_active` before running any check."
After: "Before Claude sends a reply, small checker scripts read it. One
checks length. Others check that every claim comes with proof. If a check
fails, Claude must rewrite. The problem: when Claude rewrote, both checkers
stepped aside completely."
`scripts/validate-pr-body.mjs` fails a Summary or Review Claim that holds a
code name and lists each one. It also blocks a Summary above reading grade 11,
with more than 25% words of three or more syllables, or over 150 words. CI
runs the same check on every PR's live text, so a body edited on GitHub is
checked too.
## Review Claim
State the one thing the reviewer is being asked to approve, in everyday
words with no code names (same rule as Summary).
## Review Lane
Choose exactly one from your `drafter.config.json` taxonomy (default:
`behavior`, `refactor`, `proof`, `cleanup`, `policy`, `docs`).
## Review Unit
Choose exactly one matching review unit from your configured taxonomy. The
unit must match the changed files — the validator rejects a declared unit
that ships files assigned to another unit.
## Safety Invariant
Explain why this slice is safe to review locally. Propose it and get the
user's confirmation before publishing (see Scope & Ambiguity Confirmation
above).
## Slice Rationale
Explain why this work is split here instead of bundled elsewhere.
## Non-goals
List what this slice explicitly does not change. For a `refactor` lane,
include one of: `no behavior change`, `behavior unchanged`, `unchanged
behavior`, or `pass unchanged`.
## Architecture
Only include this section when the change modifies component interactions,
control flow, state flow, or data flow. Quote Mermaid labels that contain
prose, punctuation, or code-ish text.
### Before
\`\`\`mermaid
graph TD
A["old flow"]
\`\`\`
### After
\`\`\`mermaid
graph TD
A["new flow"]
\`\`\`
## Measured
Command: `exact repro command`
- base: `the command's real output on the base, pasted`
- head: `the command's real output on this branch, pasted`
A slice with nothing to measure says so and says why: one line,
`none: <reason>`, takes the place of the rows.
## Test Plan
<details>
<summary>Test Plan</summary>
- [ ] exact command
- [ ] exact command
- Not run: the check that was skipped. Blocker: what stops it
</details>
## Visual Proof
Required when the diff changes UI-impacting files. Include before/after
screenshots or a video link.
## Revert Plan
<details>
<summary>Revert Plan</summary>
- Safe to revert? Yes/No
- Revert command: `git revert <sha>` or equivalent
- Post-revert steps: None / concrete steps
- Data migration? No / concrete steps
</details>
```
If the change is small and has no architectural impact, omit `## Architecture`
rather than forcing filler.
Use visible markdown sections for review metadata. Do not hide `Review
Claim`, `Review Lane`, `Review Unit`, `Safety Invariant`, or `Slice Rationale`
inside `<details>` or other HTML disclosure blocks — they must render
directly in the PR body. Test Plan and Revert Plan are the opposite: keep
their headings visible, but their content must sit inside a collapsed
`<details>` block with `<summary>Test Plan</summary>` /
`<summary>Revert Plan</summary>`.
`## Measured` is required and stays visible. A repro command in a PR body
comes with the output it printed. Give a `base:` row and a `head:` row, each
with that output pasted inline in backticks or in a code block under the row.
A slice whose claim is about speed, size, or cost shows the measured number on
both rows. A slice with nothing to measure writes `none: <reason>` instead.
`scripts/validate-pr-body.mjs` reads the shape only: the heading is there, it
is outside `<details>`, and either both rows carry pasted output or the
`none:` line has a reason. It does not read what the words mean, so a number
in another section never counts, and nothing judges whether a `none:` reason
is true; the validator prints that reason as not checked. `none:` is not a way
around a slice that claims a number.
A check the Test Plan names but that was not run gets its own row that starts
`Not run:`, and that row names what stops it: `Blocker: <what stops it>` on
the same line or on the next non-empty line. A check that nothing stops is
run, and its result is pasted, before the PR is published. The validator
rejects a `Not run:` row with no blocker. It reads the shape of that one
labelled row only: the row starts with `Not run:` and `Blocker:` is followed
by text. It does not hunt the rest of the body for other ways of saying a
check was skipped, and nothing judges whether a blocker is real or could have
been removed; the validator prints each blocker as not checked. Drop the row
from the example above when every check ran.
Do not default to a lightweight `## Summary / ## Testing / ## Notes` PR body.
Use the schema above as the floor.
## Visual proof rules
If the change is UI-impacting, capture before/after screenshots or a video
first. UI-impacting means the user-visible experience changes, even when no
file under a conventional UI directory changes.
- Visual proof must show the changed behavior itself, not just the changed
screen area. Open every screenshot or video and verify the user-visible
target is present and identifiable.
- If the changed behavior spans multiple states or a state transition —
restart persistence, before/after transitions, progress animations, opening
then dismissing overlays — use animated proof (gif/mp4/webm/walkthrough
video). Static screenshots alone are not enough.
- When the claim is that a state persists across an action (navigation,
restart, refresh, resize), each screenshot must also show the action
happened, with an on-screen cue distinguishing each step.
- Caption each visual proof item with the concrete thing the reviewer should
see.
- Before/after proof images must use distinct local filenames before upload
(a shared upload prefix keyed by basename can otherwise collapse two
different images into one URL).
## Diff atomicity gate
Run `lintDiffAtomicity()` (or the CLI at `scripts/lint-diff-atomicity.mjs`)
before publishing. Fatal findings — generated files mixed with hand-written
source, an orphaned lockfile change, a debugger statement, a focused
(`.only`) test — are hard failures; split the PR to clear them. Warnings
(readability findings like file count or unrelated-area spread) are
advisories — confirm the spread is intentional rather than silently ignoring
them.
If one branch mixes behavior, refactor, cleanup, or proof/test-harness work,
split the work into separate PRs. Do not relabel the lane or weaken the
checker to make a mixed branch pass.
## Command surface
The scripts below import `@neko-catpital-labs/drafter-core`. Install it once
in the repo you're drafting PRs for (`npm install --save-dev
@neko-catpital-labs/drafter-core`) so Node can resolve the import — a globally
installed skill copy can't resolve a bare import on its own.
1. Branch from your canonical base remote (see `references/branching-workflow.md`).
2. If the repo has a make-pr overlay, run its **commit-scoped preflight**
(dry-run) and learn the Review Unit **before** writing the body.
3. Choose one publish path before the first push: Mergify stack tooling when
the repo uses it (for example `.mergify.yml`), otherwise `gh pr create` /
`gh pr edit`. Do not switch mid-slice.
4. Draft from the canonical template and validate it:
```bash
npx @neko-catpital-labs/drafter-mcp --help # or call validate_pr_body via MCP
node scripts/validate-pr-body.mjs --body-file /tmp/my-pr.md --base main
```
5. Push the working branch and create or update the PR only via the path
chosen in step 3, once this skill's schema and validation pass. The
command is not a license to skip the schema for a Summary / Test plan
template.
After any branch update, rebase, or force-push, refresh the PR title and body
so they still match the live diff. Re-check `## Summary`, test commands,
revert guidance, and any visual proof section; old copy is stale the moment
the branch meaning changes.
## Upstream-first workflow
Use the canonical repository as the PR target and an explicit publish remote
for branch publication. Create branches from `<baseRemote>/<base>`; open PRs
against the canonical repository's base branch. See
`references/branching-workflow.md`.
## Merge-queue tools (Mergify, etc.)
If this repo uses a merge-queue or stacked-PR tool, choose that path once
before the first push and follow that tool's publish workflow after this
skill produces a validated body/branch. Do not also open the same slice with
a separate `gh pr create` unless the make-pr overlay says to refresh
title/body on an already-pushed stack.
## Validation
- Ensure the PR title still matches the current slice after any branch update
or force-push.
- Ensure the `## Summary` section still describes the current diff.
- Ensure the branch is pushed.
- Ensure body sections are present and concrete; test commands are real
commands that were actually run when possible; revert guidance is honest.
- Keep Test Plan and Revert Plan content inside their collapsed `<details>`
blocks.
- Give every `Not run:` row in the Test Plan a `Blocker:` that says what
stops the check, or run the check and replace the row with its result.
- Keep measured proof in plain view: the output a repro command printed on
the base and on the head sits under `## Measured`, outside any collapsed
block, or that section says `none: <reason>`.
- Do not create or update a PR when the branch has no file changes against
its selected base, or contains an empty commit slice.
- For UI-impacting diffs, include `## Visual Proof` before publishing;
classify by user-visible behavior, not by path alone.
## References
- `references/branching-workflow.md`
- `scripts/validate-pr-body.mjs`
- `scripts/lint-diff-atomicity.mjs`
- `scripts/pr-body-template.mjs`
- `scripts/measured-section.mjs`