agentleFS
sourcehawksourcehawk/feature-dev-workflow/skills/opening-a-pull-request/SKILL.md

opening-a-pull-request

Skill2 starsChanged 12 days agoMITView on GitHub

Three moments: Two templates carry the shape and the per-section guidance: Copy the appropriate template, fill in each section per its <!-- --> guidance, then pass the body to gh pr create (opening) or gh pr edit (flipping or editing) via a --body "$(cat <<'EOF' ... EOF)" heredoc. GitHub doesn't render HTML comments, so leaving the template guidance in place is harmless — don't burn a step removing it. OPTIONAL SUB-SKILL: simple-english:simple-english for the prose of the body. If it…

  • Commits and pushes

The file

We split this skill into the rules it gives an agent

8 rules. About 0 are common, 6 are distinctive, and 2 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈6

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 2

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Opening a draft PR: when work is still in flight and you want an early surface for reviewers to flag direction issues.

    line 13
  2. Distinctive:

    Reconciling an open PR's body: when commits have landed since the body was last written and it no longer matches what the diff does — …

    line 15
  3. Very specific:

    When the PR has a tracking issue, link it as the first line of the body's opening section — ## What lands here for draft, ## Description …

    line 53
  4. Distinctive:

    If there is no tracking issue, drop the line entirely and open the section with prose.

    line 66
  5. Distinctive:

    Don't carry the issue link as a bold-line metadata header at the top of the body — the keyword form is what GitHub uses to thread the …

    line 68
  6. Very specific:

    Run gh pr create or gh pr edit only with the user's approval, which takes one of two forms:

    line 74
  7. Distinctive:

    Every confirmation shows the user:

    line 81
  8. Distinctive:

    All of these mean: rewrite the body from the right template and paste it inline in chat.

    line 136

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “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

opening-a-pull-request

When to invoke

Three moments:

  • Opening a draft PR when work is still in flight and you want an early surface for reviewers to flag direction issues. Body follows the draft shape (below).
  • Flipping to ready, or opening a PR straight to ready, when the work is done. Body follows the ready shape.
  • Reconciling an open PR's body when commits have landed since the body was last written and it no longer matches what the diff does — review-driven changes, follow-up commits, a scope shift (§Reconciling an open PR's body with reality). Body only; no comment.

Templates

Two templates carry the shape and the per-section guidance:

  • ${CLAUDE_PLUGIN_ROOT}/skills/opening-a-pull-request/templates/pull-request-draft.md: draft PR body.
  • ${CLAUDE_PLUGIN_ROOT}/skills/opening-a-pull-request/templates/pull-request-ready.md: ready-for-review PR body.

Copy the appropriate template, fill in each section per its <!-- --> guidance, then pass the body to gh pr create (opening) or gh pr edit (flipping or editing) via a --body "$(cat <<'EOF' ... EOF)" heredoc. GitHub doesn't render HTML comments, so leaving the template guidance in place is harmless — don't burn a step removing it.

OPTIONAL SUB-SKILL: simple-english:simple-english for the prose of the body. If it is available, load it before you draft and write the sentences under it. Where its formatting rules (headings, bold, lists, or its register for chat replies) differ from the host skill or its template, the host skill and its template win. It sets sentence style only: the template and this skill still decide the sections, their order, and their length. The title follows §PR title, not this skill. If the skill is not available, do not stop or wait for it. If you have not already done so in this session, tell the user once that it can be installed with /plugin marketplace add AminBlg/SimpleEnglish and then /plugin install simple-english@simple-english. Then write short, plain, active sentences without it.

PR title

Set the title once when opening and don't rename it. Match the project's commit-message convention from CLAUDE.md:

<type>(<area>): <imperative summary>

Types: feat, fix, refactor, test, chore, docs. Area mirrors the module path (api, auth, ui, db). When the PR bundles unrelated areas, lead with the headline change and acknowledge the others in the body — don't try to encode both in the title.

The summary names the effect or the cause, whichever a human recognizes faster. Choose one:

ShapeReads asUse when
Impact-firstfix(api): stop stale sessions from blocking every newer oneThe reader cares what starts or stops happening.
Cause-firstfix(api): expire sessions whose backend lookup never answersThe trigger is the surprising part and names the bug better than its effect does.

If both read equally well, take impact-first. The title is the squash-merge subject and the line that appears in every notification, release note, and git log, read by people who know the codebase but have never seen this diff. Keep the whole thing inside roughly 72 characters so it survives those surfaces without truncation — if it won't fit, the summary is carrying detail that belongs in the body.

The failure to avoid is the third shape, the mechanic: fix(api): move the expiry check above the backend lookup. It names the code motion that achieved the fix. It is accurate, it satisfies every part of the convention above, and it still leaves the reader unable to tell whether the PR matters to them — because the code motion is the one thing the diff already shows. Check the drafted summary against this: does it say what changed for the system, or only what changed in the code? A title whose meaning only arrives once the diff is open has failed.

Do not suffix the title with lifecycle wording (wip, draft, plan, scaffolding, etc.). GitHub's draft / ready chip carries the lifecycle state. A single title that survives from open through merge avoids renames and avoids shipping stale wording into the merged record.

Linking the tracking issue

When the PR has a tracking issue, link it as the first line of the body's opening section — ## What lands here for draft, ## Description for ready. The exact keyword depends on whether the PR should close the issue on merge:

  • Fixes #<num> — bug-fix issue; GitHub auto-closes the issue when the PR merges to the default branch.
  • Closes #<num> — feature/task issue; same auto-close semantics, neutral phrasing.
  • Towards #<num> — the PR contributes to the issue but should NOT close it on merge. GitHub creates the back-link but doesn't auto-close. Use when the issue will be closed by a sibling PR, by a later PR, or by the orchestrator manually (see below).

Which keyword belongs depends on which branch the PR targets:

  • PR targets main (the default branch) — use Fixes / Closes if the merge should close the issue; use Towards if the issue should stay open. This includes sub-PRs in the sub_pr_target: main model (see feature-dev-workflow:fanning-out-with-worktrees): each sub-PR targets main directly with the type-appropriate closing keyword (Fixes #<sub-issue> for a bug sub-issue, Closes #<sub-issue> otherwise); no sub-PR closes the epic, and the orchestrator leaves it open for the user to close.
  • PR targets a feature branch (<type>/<slug> in the multi-PR feature-branch model — see feature-dev-workflow:developing-a-feature) — use Towards #<sub-issue>. Fixes / Closes keywords only auto-trigger on merges to the default branch, so writing them on a feature-branch-bound PR creates a misleading promise that nothing will fulfill. The sub-issue is closed manually by the orchestrator after the self-merge. The integration PR (<type>/<slug> → main) gets Closes #<epic> because that PR does merge to main.
  • PR temporarily targets a sibling branch and will be retargeted to the feature branch (stacked sub-PRs in the sub_pr_target: feature-branch model) — use Towards #<sub-issue>, as for any PR whose final base is the feature branch: the eventual merge does not land on the default branch, so no closing keyword fires, and the orchestrator closes the sub-issue by hand after the merge.
  • PR temporarily targets a sibling branch and will be retargeted to main (stacked sub-PRs in the sub_pr_target: main model: each draft opens against its parent branch so its diff shows only its own commits, then retargets to main when the parent merges; gh stack sets and corrects these bases, see feature-dev-workflow:stacking-dependent-prs) — use the type-appropriate closing keyword (Fixes #<sub-issue> for a bug sub-issue, Closes #<sub-issue> otherwise) from the start. It describes the merge that will eventually happen on main, and it survives the retarget with no body edit; a Towards placed "because the base isn't main yet" has to be remembered and upgraded at every retarget, and a forgotten upgrade means the sub-issue never auto-closes. Know what the keyword does NOT do while the base is a sibling branch: GitHub only creates the issue's linked-PR association (and only auto-closes) for closing keywords on default-branch-base PRs, so until the retarget the sub-issue shows a plain timeline mention and no linked PR — deferred, not broken. At each retarget (after gh stack sync once the parent merged), verify the base is main and the linkage materialized (gh pr view <num> --json baseRefName,closingIssuesReferences shows main and lists the sub-issue); if it stays empty, re-save the body so GitHub re-evaluates the keyword against the new base.

If there is no tracking issue, drop the line entirely and open the section with prose.

Don't carry the issue link as a bold-line metadata header at the top of the body — the keyword form is what GitHub uses to thread the cross-link and it reads as a natural opener to the implementation summary.

Other related PRs / issues (siblings, follow-ups, prior art) belong under ## Related — that section is only for links that aren't the tracking issue, since the tracking issue is already cross-linked via the keyword above.

Core principle: user-in-the-loop for every GitHub mutation

Run gh pr create or gh pr edit only with the user's approval, which takes one of two forms:

  • A standing grant that names this kind of action: an explicit instruction in the conversation ("open the PRs as drafts without asking me"), a rule in the project's instructions file or memory, or configuration recorded in the state file (for example sub_pr_approval: autonomous for the sub-PR bundle). Under a grant, act without a prompt, then show the target and the full body in your reply so the user sees what landed.
  • A fresh confirmation for the specific body about to land, whenever no grant covers the action. Generic intent earlier ("yes please open a PR") is not a grant: it says what the user wants done, not that they waive their look at the body.

A grant covers the kind of action it names and nothing wider. A grant to open drafts does not cover flipping a PR ready or rewriting another PR's body. No grant covers the feature's final merge to main, the single-PR feature PR or the integration PR; sub-PR merges follow the state file's recorded configuration (see the merge guard in feature-dev-workflow:developing-a-feature Step 6).

Every confirmation shows the user:

  • The exact target (the repo, or #<num> for edits).
  • The full proposed body.

Without a grant, wait for an explicit "yes" before any gh pr call. Treat absence of objection as a no.

Steps when flipping a draft to ready

  1. Rewrite the body from ${CLAUDE_PLUGIN_ROOT}/skills/opening-a-pull-request/templates/pull-request-ready.md. The shapes are different — the draft asks "review the direction"; the ready asks "review the implementation." Don't ship the draft body forward unchanged.
  2. Confirm both mutations in one prompt, body inline. Marking the PR ready is a separate GitHub mutation from editing the body, and the user-in-the-loop rule applies to both. Phrase the confirmation as: "About to update #<num>'s body to the version below AND flip it from draft to ready. Confirm?" — then paste the body. Wait for an explicit yes. Splitting confirmation across two prompts is fine; running gh pr ready on the strength of the body confirmation is not. A standing grant replaces this prompt only if it names flipping PRs ready; then show the body and the flip in your reply.
  3. Run gh pr edit <num> --body "$(cat <<'EOF' … EOF)" then gh pr ready <num> once the user confirms (or under that grant).

Reconciling an open PR's body with reality

Opening and flipping aren't the only moments the body has to be true. Commits land after the body was written — review-driven fixes, follow-ups, a scope shift — and the body drifts from what the diff actually does. A PR whose body contradicts its diff misleads the reviewer in the first ten seconds. This moment is independent of lifecycle state: an already-ready PR that is not being flipped still gets reconciled.

The materiality test. Would a reviewer reading the body be misled by what the diff now does? If yes, reconcile. If the commits since are immaterial to what the body claims (a typo fix, a rename the body never named), leave it.

Body only — no comment. Unlike a tracking issue, a PR does not get a decision-trail comment. The asymmetry is deliberate: the issue is the durable planning record, so why a decision changed belongs in its thread (feature-dev-workflow:writing-github-issues Step 2D). The PR already carries its "why" in the commit history and the review threads; the body's only job is to describe the final state accurately. Recording the delta a second time on the PR is noise.

The mechanic:

  1. Rewrite the affected sections from the template the PR already uses (draft or ready) so they describe what the branch does now. "Match the diff" means the body's claims hold for the branch, not that the body retells the diff: each new commit's change is written as the template asks, as a difference a user or an operator will notice, with what it replaces. Leave the opening issue-link keyword and the title unchanged — neither the linkage nor the type/area changed. Do not flip the lifecycle state; this is a body edit, not a gh pr ready.
  2. Confirm the exact body (§Core principle) — paste it inline under an "About to update #<num>'s body to match the diff — body only, PR stays as-is. Confirm?" line, and wait for an explicit yes. Under a standing grant for PR body edits, skip the prompt and show the new body in your reply.
  3. Run the single mutation once confirmed: gh pr edit <num> --body "$(cat <<'EOF' … EOF)".

Anti-patterns

  • Lifecycle suffix in PR titles (... wip, ... draft, ... scaffolding). The title outlives the state that named it. The body and GitHub's chip carry lifecycle; the title doesn't need to.
  • Mechanic-first titles (... move the check above the lookup, ... reorder the gates, ... extract the handler). They describe the code motion, which is the one thing the diff already shows. Lead with the effect or the cause (§PR title).
  • Change lines that retell the diff (The handler reads the timeout from the config, The sync loop re-queues each entry every 10 minutes). The title's mechanic failure, one level down: each line names a component and what it does, and the reader still cannot tell what they will see differently. Write each line as the difference a user or an operator will notice, and what it replaces (the template's guidance for ## What lands here or ## Changes).
  • Challenges as the body's overflow section. Consequences, tradeoffs, follow-up work, and the history of review rounds all get filed there because the heading is sitting in the template. Each has a home: the Description's third part, Related, or the commits. The section earns its place only when the diff hides a system fact a reviewer needs — otherwise the heading gets deleted.
  • Flipping ready with the draft body unchanged. Different shape, different audience. Rewrite from the ready template.
  • Marking ready before the Testing section is filled in. That section is what gives the reviewer confidence the PR is shippable; leaving it blank silently drops the claim.
  • Running gh pr create / gh pr edit on inferred consent. Inferred consent is earlier intent read as a waiver; a standing grant is the user saying outright that this kind of action needs no prompt. Without a grant, every body is a fresh confirmation. The cost of pausing is low; the cost of an unwanted public mutation is high.
  • Leaving a ready PR's body stale because it's "already open." Opening and flipping aren't the only moments the body matters; commits that land afterward change what the reviewer should read. A body that contradicts the diff gets reconciled (§Reconciling an open PR's body with reality) — body only, no comment, the body just has to match reality.

Red flags: STOP before flipping ready or publishing

These thoughts mean the PR isn't actually ready to publish or flip:

ThoughtReality
"The draft description is fine, no need to rewrite"Different shape, different audience. Rewrite from the ready template.
"Marking ready now, will fix the body in a follow-up edit"The body is what the reviewer reads in the first 10 seconds. Fix it first, then gh pr ready.
"The user said yes a turn ago, this is the same thing"A yes to one body is not a grant. Bodies change between turns: unless the user granted this kind of action outright, confirm the exact body about to land.
"They told me to open drafts without asking, so flipping ready is covered too"A grant covers the action it names. Flipping ready needs its own grant or confirmation, and the feature's final merge to main is always the user's.
"I'll just append a note and they can edit later if needed"They shouldn't have to clean up after the agent. Confirm first.
"The PR's already open/ready, the stale body isn't worth re-editing"The body is what the reviewer reads first; once the diff moves past it, it misleads. Reconcile the body to match the diff — body only, no comment (§Reconciling an open PR's body with reality).
"The sub-issue shows no linked PR, the keyword must be wrong"Closing-keyword linkage only materializes while the PR's base is the default branch. On a stacked draft it's deferred until retarget — verify with gh pr view <num> --json closingIssuesReferences then, not before.
"The title is accurate and follows the convention, so it's fine"Accuracy is not the bar; recognition is. If the summary names the code motion, a reader can't tell whether the PR matters to them without opening the diff. Lead with the effect or the cause.
"This part was genuinely hard, so it belongs in Challenges"Hard for the author is not the test. The test is whether the diff hides a system fact the reviewer needs. Difficulty you already resolved, review rounds, and wrong turns live in the commits.
"Better to include it than leave the reviewer guessing"A reviewer's attention is finite and spending it on filler costs the sections that matter. Every fact needs a section that's actually for it, or it comes out.

All of these mean: rewrite the body from the right template and paste it inline in chat. Wait for an explicit yes, unless a standing grant covers this kind of action; then act and show the body in your reply.

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.