| name | pr-creation |
| description | Creates high-quality pull requests with an iterative compress-critique-fix loop before submission. Activate this skill whenever you are asked to create, open, submit, or push a pull request, OR whenever a new feature, fix, or refactor is complete and ready to ship. Also activate when the user says "make a PR", "open a PR", "submit this for review", "push and create a PR", "I'm done, create the PR", "the feature is done", "I'm finished", or any variation of completing work / requesting a pull request. Always activate before running `gh pr create`.
|
Pull Request Creation Skill
IMPORTANT: Always follow this skill before creating any PR. Do not skip steps, especially the iterative compress-critique-fix loop.
When to Use
Activate this skill when the user says any of the following (or similar):
- “Create a PR” / “Create a pull request”
- “Open a PR” / “Open a pull request”
- “Make a PR for this”
- “Submit this for review”
- “Push and create a PR”
- “I’m done, create the PR”
- “Can you PR this?”
- “Send this up for review”
- “The feature is done” / “I’m finished” / “Ship it”
Also activate when:
- You have just finished implementing a new feature, fix, or refactor—run the loop, then create the PR
- The user asks you to submit completed work
- CLAUDE.md or task instructions say to create a PR when done
Do NOT use this skill for:
- Reviewing an existing PR (use
gh pr view or gh pr diff instead)
- Merging a PR (
gh pr merge)
- Updating a PR description only (just run
gh pr edit)
Prerequisites
- GitHub CLI (
gh) must be authenticated
- All changes must be committed to a feature branch (not
$CLAUDE_CODE_BASE_REF/master)
Updating an Existing PR
Before updating an existing PR (pushing new commits, editing the description, etc.), you MUST check its current status:
- Run
gh pr view <pr-number> --json state to check the PR state
- Based on the result:
- Open: Proceed with the update normally
- Merged: Do NOT update it. Create a new PR instead with the additional changes
- Closed (not merged): Ask the user what they’d like to do, if not already clarified
Workflow
Step 1: Gather Context
- The base branch is in the env variable
$CLAUDE_CODE_BASE_REF
- Run
git diff <base-branch>...HEAD to see all changes
- Run
git log <base-branch>..HEAD --oneline to see all commits
- Review the changed files to understand the scope
- Check for PR description guidance—look for
CONTRIBUTING.md, .github/PULL_REQUEST_TEMPLATE.md, or similar files in the repo. If found, read them and adapt the PR description to follow the repository’s conventions (see pr-templates.md for details)
Step 2: Iterative Compress-Critique-Fix Loop
Before creating the PR, run an iterative loop until you reach a fixed point—a full critique pass that turns up nothing worth changing. This is the same loop described in CLAUDE.md’s Self-Critique Loop section; apply it here on the full diff.
You MUST read .claude/skills/pr-creation/critique-prompt.md once before the first pass—it contains the detailed checklist the sub-agent needs.
Each pass:
- Launch a critique sub-agent using the Task tool:
subagent_type: “general-purpose”
description: “Critique code changes”
prompt: Include the full diff (git diff $CLAUDE_CODE_BASE_REF...HEAD) and the critique prompt from the resource file
- For each issue raised, assess validity, then take the easy wins first:
- Compress—delete dead code, unused imports, commented-out blocks, WHAT-comments, backwards-compat shims, premature abstractions
- Readability—tighter names, un-nest conditionals, combine related checks, guard-clause early returns
- Code reuse—extract duplicated logic into helpers; search for existing utilities before adding new ones
- Parametrize tests—collapse near-identical tests into a single parametrized/table-driven test with exact-equality assertions
- Fixtures—pull repeated setup/teardown into shared fixtures
- Correctness—bugs, edge cases, security, swallowed errors
- Commit the fixes (Conventional Commits format, per
CLAUDE.md)
- Start a fresh critique pass—the previous output is now stale
Stop when a full pass returns no actionable issues. Cap at ~5 passes; if issues are still being found at pass 5, stop, summarize what’s left, and ask the user how to proceed rather than looping silently.
Skip the loop for trivial changes (typo fixes, single-line config tweaks, pure docs edits)—say so explicitly when you skip.
Step 3: Stress-Test Infrastructure Changes
If the diff includes changes to infrastructure components (sandbox config, container orchestration, firewall rules, CI workflows, entrypoint hardening, domain allowlist), run /stress-test for those components before proceeding — unless the diff already includes comprehensive tests for the changed infrastructure. The stress-test skill generates both static config validation and live runtime checks, and runs its own iterative critique-fix loop on the test code.
Step 4: Verify Critical-Codepath Coverage
Before running validation, audit the diff for critical codepaths that lack a test which would fail if the path broke — coverage that executes a line is not the same as a test that pins its behavior.
Walk every new or changed branch and apply the litmus: mutate or invert this branch — does a test go red? Pay special attention to the paths that line coverage and happy-path tests routinely miss:
- Error / fail-loud paths — the
exit 1 with guidance, the "refuse and stop" branch. Assert the exit code and the message, not just that it didn't crash.
- Auto-detect / fallback / degrade arms — logic that picks a path from the environment (a capability probe, an OS/arch check, a "no sudo → degrade" gate). Test each arm with the real function over stubbed inputs; an override flag exercising one arm does not cover the auto-detected arm.
- Cleanup / teardown / idempotency wiring — the line that removes stale state, clears a pin, reaps a volume. If a test harness stubs the cleanup to a no-op, the real effect is unverified — drive the real function and assert the state change.
- Enumerated members — each regex alternative, allowlist entry, or dispatch arm needs a case (see CLAUDE.md Testing).
For any path that is only exercised incidentally (a happy-path run that happens to touch it, a stubbed-out helper), add a focused test that fails when that path is broken before proceeding. If a path is genuinely only reachable in a live/integration environment (a real container launch), say so explicitly and name the unit-level proxy that guards its logic — don't silently leave it untested. When a branch is hard to test, that difficulty usually points at a refactor (extract the branch into a sliceable function) — do that rather than skipping the test.
Don't try to analytically predict a coverage gate's verdict when you can't run the tool locally — write the obvious branch tests, push, and let the gate report the exact uncovered lines. Tracing which feeder/harness drives a coverage gate (e.g. reverse-engineering how kcov's argv0 interception reaches a sourced-only bash lib) to decide whether your new lines are covered is a deep, low-confidence rabbit hole. The gate names every uncovered line authoritatively in seconds; your local reasoning does not. So when the coverage tool isn't installed here (kcov, a mutation runner), add a focused test per new branch — those are correct regardless of the gate's mechanics — then push and read the gate, rather than spending the time guessing. This is the coverage-gate case of the general "push earlier, let CI surface issues" rule (Step 9).
Step 5: Run Validation
Run the project’s test/lint/typecheck commands (see pr-templates.md for common commands per language). Fix any failures before proceeding. If validation surfaces new defects, loop back into Step 2 with the fixes included.
Step 6: Push and Create the Pull Request
You MUST read pr-templates.md for the PR template and formatting guidelines before this step.
- Push the branch:
git push -u origin HEAD
- Check if a PR already exists for the current branch:
EXISTING_PR=$(gh pr list --head "$(git branch --show-current)" --json number --jq '.[0].number' 2>/dev/null)
If a PR already exists, update it with gh pr edit instead of creating a new one.
- Create the PR using
gh pr create with the template from the resource file. Make sure that you use the target branch
- Add the
release label when this PR should cut a release. The release label triggers release-prep.yaml: it classifies the pending changelog.d/ fragments into a conservative semver bump and commits the version bump + assembled CHANGELOG roll onto the PR branch, and the matching vX.Y.Z tag is pushed post-merge by tag-release.yaml. Apply it (gh pr edit <pr-number> --add-label release) only when the intent is to publish a new version with this merge — a user-facing change that should ship now. Cutting a release is a deliberate decision, so when it isn't explicit, ask the user before labeling; an unlabeled PR just accumulates its fragments until a later release PR.
Step 7: Update PR Title and Description (after any post-creation changes)
If you made any commits after creating the PR (from critique, validation, or CI failures), always update the PR title and description to reflect the final state of all changes:
- Re-read the diff (
git diff $CLAUDE_CODE_BASE_REF...HEAD) and commit log (git log $CLAUDE_CODE_BASE_REF..HEAD --oneline) to see the full scope
- Rewrite the title and body to accurately describe the current totality of changes, not just the original scope:
gh pr edit <pr-number> --title "<type>: <updated description>" --body "$(cat <<'EOF'
<updated body using template from pr-templates.md>
EOF
)"
Skip this step if no commits were made after Step 6.
Step 8: Wait for CI Checks (MANDATORY)
- Run
gh pr checks <pr-number> --watch to monitor
- If any checks fail, investigate and fix the issues
- Push fixes, update the PR description (Step 6), and wait again
- Only proceed once all checks are green
Step 9: Report Result
Provide the PR URL and confirm all CI checks have passed.
Step 9: Iteration Retrospective
After reporting the result, briefly reflect on how you could have iterated faster on this task. Consider:
- Parallelization: Which investigations, tool calls, or sub-agent launches could have run in parallel instead of sequentially?
- Targeted checks over full sweeps: Were there broad searches or full test runs you ran locally that CI would have caught anyway? Could a more targeted check (single file, single test, quick lint) have been faster?
- Earlier CI delegation: Could you have pushed earlier and let CI surface issues rather than exhaustively validating locally first?
- Critique loop efficiency: Did any critique passes surface issues that a quick re-read would have caught before launching the sub-agent?
State each insight as one concrete line. Skip this step if the task was trivial (single-file, no iteration needed).
Examples
Example 1: Simple Bug Fix
User says: “I’m done fixing the login bug, create a PR”
Claude’s actions:
- Runs
git diff $CLAUDE_CODE_BASE_REF...HEAD—sees changes in src/auth/login.ts and tests/auth/login.test.ts
- Runs
git log $CLAUDE_CODE_BASE_REF..HEAD --oneline—sees 2 commits
- Launches critique sub-agent with the diff
- Critique returns: “Looks good, minor suggestion to add null check on line 42”
- Fixes the null check, commits:
fix: add null check for empty session token
- Runs
pnpm check && pnpm test && pnpm lint—all pass
- Pushes and creates PR:
gh pr create --title "fix: handle null session token in login flow" --body "..."
- Updates PR description to reflect the null-check fix added during critique
- Watches CI with
gh pr checks 47 --watch—all green
- Reports: “PR #47 created and all CI checks pass: https://github.com/org/repo/pull/47"
Example 2: Multi-Commit Feature
User says: “Submit this for review”
Claude’s actions:
- Runs
git diff $CLAUDE_CODE_BASE_REF...HEAD—sees changes across 8 files including new components, tests, and API routes
- Runs
git log $CLAUDE_CODE_BASE_REF..HEAD --oneline—sees 5 commits
- Pass 1: Critique flags 4 issues—unused import, two near-identical tests that should parametrize, duplicated validation logic across 2 components, an over-engineered single-caller wrapper. Fixes them: deletes the import, collapses the tests with
it.each, extracts a shared validateInput helper for the duplication, inlines the single-caller wrapper. Commits.
- Pass 2: Critique flags 2 more—a leftover WHAT-comment from the refactor and a nested conditional. Un-nests and removes the comment. Commits.
- Pass 3: Critique returns clean—fixed point reached, exit loop.
- Runs validation—all pass
- Pushes and creates PR with detailed body summarizing the feature
- Updates PR title and description to reflect all changes including critique fixes
- Watches CI—one check fails (lint warning on new file)
- Fixes lint issue, pushes, updates PR description again—all green
- Reports success with PR URL
Example 3: When Input Is Unclear
User says: “Push this up”
Claude asks: “I see you have changes on branch feat/user-dashboard. Would you like me to create a pull request against $CLAUDE_CODE_BASE_REF, or just push the branch without creating a PR?”
Error Handling
- Critique finds issues: Fix them before proceeding—do not skip
- Tests fail: Fix the tests, don’t skip them
gh not authenticated: Tell user to run gh auth login or set GH_TOKEN
- Push fails: Check branch permissions and remote configuration
- PR already exists (HTTP 422): Check for existing PRs first with
gh pr list --head "$(git branch --show-current)", then use gh pr edit to update
- No changes to PR: Confirm with the user that work is committed