| name | pr-lifecycle |
| description | Enforce project hygiene throughout the PR lifecycle. Use before creating PRs to validate worktrees, branch naming, and issue linking. Use when creating PRs to ensure proper "Fixes |
PR Lifecycle Management
Enforce project hygiene throughout the pull request lifecycle. This skill provides checklists, validation commands, and templates to ensure PRs properly close issues and follow project conventions.
Critical Constraints - Session Statelessness
IMPORTANT: You are operating in a stateless session. Each Claude Code session is isolated.
You CANNOT:
- Track issues across sessions
- Remember to do something later
- Follow up on tasks in the future
- Promise to handle something "in a follow-up"
You MUST NOT say things like:
- "I'll track this in a follow-up issue"
- "I'll remember to fix this later"
- "I'll handle this in a subsequent PR"
PR Comment Response Decision Framework
When addressing review comments, choose ONE of these options:
Option 1: Implement Now (Preferred)
When: Change is small-ish and worth doing. Action: Implement fix, commit, reply with: "Fixed in commit {sha}: {description}"
Option 2: Defer with Issue (Expensive - use sparingly)
When: Change is large AND worth doing AND not critical to PR. Action:
- Create GitHub issue NOW using
gh issue create - Add to same milestone as PR's issue
- Add as sub-issue using
gh sub-issue add - Reply: "Created #XYZ to track this. Not addressing in this PR because {reason}."
Option 3: Disagree / Won't Fix
When: Suggestion is nitpicky, negligible, or you disagree. Action: Reply explaining why not addressing. Be professional. NEVER use for: Test failures, errors, security concerns.
Overview
Project Hygiene Invariant: Each PR should:
- Close exactly one GitHub issue
- Include "Fixes #XYZ" (or similar keyword) in PR body
- Be created from a proper worktree using the
.claude/skills/git-worktreesskill (you should not be in/workspace, which should always be kept up to date with origin main - instead you should be inwip/<feature branch>) - Follow branch naming convention:
m<milestone_id>-p<parent_issue_id>-i<issue_num>-<slug>(with appropriate variations) - Use Conventional Emoji Commits for PR title
Why This Matters: PRs #221 and #222 didn't include "Fixes #XYZ" keywords, causing issues to remain open after merge. This skill prevents such "orphan PRs."
Tmux Window Naming Convention
When working in a tmux session, window names reflect the current PR lifecycle phase for quick visual reference.
Format: <emoji><prefix><number>
Prefix Conventions:
i= Issue number (e.g.,๐ปi483= coding on issue #483)pr= Pull request number (e.g.,๐งpr485= maintaining PR #485)
Examples:
๐ปi483- Coding on issue #483๐i483- Submitting PR for issue #483๐งpr485- Maintaining PR #485โณpr485- Waiting for tests on PR #485
Phase Emojis:
| Phase | Emoji | When Used | Format Example | Command |
|---|---|---|---|---|
| 1. Gathering information | ๐ | Research/discovery | ๐i483 |
Manual |
| 2. Coding | ๐ป | Active development | ๐ปi483 |
/gh-start-issue |
| 3. Waiting for tests | โณ | CI/CD checks running | โณpr485 |
/pr-workflow |
| 4. Waiting for user | โ | Needs input/clarification | โi483 or โpr485 |
Manual |
| 5. Submitting PR | ๐ | Creating/pushing PR | ๐i483 |
/pr-workflow |
| 6. PR maintenance | ๐ง | Addressing feedback/fixes | ๐งpr485 |
/pr-workflow |
| 7. Cleanup | ๐งน | Post-merge cleanup | ๐งนpr485 |
Manual |
Automatic Updates:
/gh-start-issuesets tmux to๐ปi<issue_num>(coding phase on issue)/pr-workflowupdates tmux through phases:๐i<issue_num>โ (PR created) โ๐งpr<pr_num>โโณpr<pr_num>โ๐งpr<pr_num>- Note: Transitions from issue number to PR number after PR is created
Manual Updates:
# Update tmux window name manually for issue work
ISSUE_NUM=<your_issue_number>
tmux rename-window "๐i${ISSUE_NUM}" # Research phase on issue
# Update tmux window name manually for PR work
PR_NUM=<your_pr_number>
tmux rename-window "๐งpr${PR_NUM}" # PR maintenance
Why This Convention:
- Information density: Maximizes useful info in limited tmux window title space (5-7 chars vs 50+)
- Clear distinction: Instantly know if you're working on issue or PR
- Visual status: Emoji provides at-a-glance phase indication
- WCAG compliance: Window title colors meet AAA accessibility standards (see
.devcontainer/.tmux.conf)
Phase 1: Before Creating PR
Pre-PR Checklist
Run these validations before creating a PR:
# 1. Verify you're in a worktree (not main)
pwd | grep -q "wip/" && echo "In worktree" || echo "WARNING: Not in wip/ worktree"
# 2. Check branch name follows convention
BRANCH=$(git branch --show-current)
echo "Current branch: $BRANCH"
# Should match: m<id>-p<id>-i<num>-<slug> or variants (p<id>-i<num>-<slug>, i<num>-<slug>)
# 3. Extract issue number from branch (look for i<num> pattern)
ISSUE_NUM=$(echo "$BRANCH" | grep -oP 'i\K[0-9]+' || echo "$BRANCH" | grep -oE '[0-9]+' | head -1)
echo "Issue number: $ISSUE_NUM"
# 4. Verify issue exists and is open
gh issue view "$ISSUE_NUM" --json state,title
# 5. Check commits for "Fixes #" keyword
git log origin/main..HEAD --oneline | grep -i "fixes #\|closes #\|resolves #" || echo "WARNING: No 'Fixes #' keyword found in commits"
Validation Details
Worktree Verification
Why: Feature work should happen in worktrees, not the main worktree.
# List all worktrees
git worktree list
# Verify current directory is in wip/
pwd | grep -q "wip/" && echo "In worktree" || echo "WARNING: Not in wip/ worktree"
Expected: You should be in a wip/<branch-name>/ directory.
Branch Naming Convention
Format variations:
- With milestone & parent:
m<milestone_id>-p<parent_id>-i<issue_num>-<issue_slug> - With parent only:
p<parent_id>-i<issue_num>-<issue_slug> - With milestone only:
m<milestone_id>-i<issue_num>-<issue_slug> - Standalone:
i<issue_num>-<issue_slug>
Examples:
m8-p123-i234-add-authenticationp123-i234-add-authenticationi42-add-authentication
Validation:
BRANCH=$(git branch --show-current)
# Check format: must contain i<number> pattern
if [[ "$BRANCH" =~ i[0-9]+ ]]; then
echo "Branch name follows convention"
else
echo "WARNING: Branch name '$BRANCH' doesn't follow convention (missing i<number>)"
fi
Issue Verification
Check issue exists and is open:
ISSUE_NUM=$(git branch --show-current | grep -oE '[0-9]+' | head -1)
# View issue details
gh issue view "$ISSUE_NUM" --json number,title,state,labels
# Verify it's open
STATE=$(gh issue view "$ISSUE_NUM" --json state -q '.state')
if [ "$STATE" = "OPEN" ]; then
echo "Issue #$ISSUE_NUM is open"
else
echo "WARNING: Issue #$ISSUE_NUM is $STATE"
fi
Commit Message Check
Verify commits reference the issue:
# Check for GitHub closing keywords in commits
git log origin/main..HEAD --pretty=format:"%s" | \
grep -iE "(fix(es)?|close[sd]?|resolve[sd]?)\s*#\d+" || \
echo "WARNING: No GitHub closing keywords found in commit messages"
GitHub closing keywords: close, closes, closed, fix, fixes, fixed, resolve, resolves, resolved
Phase 2: Creating PR
PR Title Convention
Format: <emoji> <type>[scope]: <description>
Common Types:
| Type | Emoji | Use For |
|---|---|---|
| feat | โจ | New features |
| fix | ๐ฉน | Bug fixes |
| docs | ๐ | Documentation |
| refactor | โป๏ธ | Code refactoring |
| test | ๐งช | Tests |
| build | ๐ง | Build system |
| perf | โก๏ธ | Performance |
| release | ๐ | Releases |
Examples:
โจ feat(cli): add deployment management commands๐ฉน fix: resolve race condition in thread handler๐ docs: add PR lifecycle skill
PR Body Template
Always include "Fixes #XYZ" to auto-close the issue:
## Summary
<1-3 bullet points describing the changes>
## Changes
- <specific change 1>
- <specific change 2>
## Related Issues
Fixes #<issue_number>
## Test Plan
- [ ] <test item 1>
- [ ] <test item 2>
---
Generated with [Claude Code](https://claude.com/claude-code)
Adding PR to Milestone
If the issue that the PR fixes is part of a milestone, add the PR to that milestone.
Create PR Command
Using gh CLI with proper body:
ISSUE_NUM=$(git branch --show-current | grep -oE '[0-9]+' | head -1)
gh pr create \
--title "โจ feat: <description>" \
--body "$(cat <<EOF
## Summary
- <summary point>
## Changes
- <change 1>
- <change 2>
## Related Issues
Fixes #$ISSUE_NUM
## Test Plan
- [ ] <test item>
---
Generated with [Claude Code](https://claude.com/claude-code)
EOF
)"
Verify PR Will Close Issue
After creating PR, verify the link:
# Get PR number
PR_NUM=$(gh pr view --json number -q '.number')
# Check closing issues
gh pr view "$PR_NUM" --json closingIssuesReferences -q '.closingIssuesReferences[].number'
# Should output the issue number
Phase 3: After PR Creation
Monitor for Automated Reviews
Copilot and other automated reviewers may add comments. Monitor and address them:
PR_NUM=$(gh pr view --json number -q '.number')
# Get owner/repo dynamically from current repository
REPO=$(gh repo view --json nameWithOwner -q .nameWithOwner)
# Check for Copilot comments (use --paginate to get ALL comments)
gh api repos/$REPO/pulls/$PR_NUM/comments --paginate \
--jq '.[] | select(.user.login == "copilot") | {id, body, path, line}'
# Check for all review comments (use --paginate to get ALL comments)
gh api repos/$REPO/pulls/$PR_NUM/comments --paginate \
--jq '.[] | {id, user: .user.login, body: .body[0:100]}'
Reply to Review Comments
After addressing feedback, reply with commit reference:
Note: Do not at-mention copilot in your reply, otherwise copilot will try to implement something.
PR_NUM=$(gh pr view --json number -q '.number')
COMMENT_ID=<comment_id>
COMMIT_SHA=$(git rev-parse --short HEAD)
REPO=$(gh repo view --json nameWithOwner -q .nameWithOwner)
gh api repos/$REPO/pulls/$PR_NUM/comments/$COMMENT_ID/replies \
-f body="Fixed in commit $COMMIT_SHA: <brief description>"
Check PR Status
PR_NUM=$(gh pr view --json number -q '.number')
# View PR checks
gh pr checks "$PR_NUM"
# View PR status
gh pr view "$PR_NUM" --json state,mergeable,reviewDecision
Phase 4: After PR Merge
Verify Issue Closure
Check if the issue was auto-closed:
ISSUE_NUM=<issue_number>
# Check issue state
STATE=$(gh issue view "$ISSUE_NUM" --json state -q '.state')
echo "Issue #$ISSUE_NUM state: $STATE"
if [ "$STATE" = "CLOSED" ]; then
echo "Issue #$ISSUE_NUM was auto-closed"
else
echo "WARNING: Issue #$ISSUE_NUM is still OPEN"
echo "Manually close with: gh issue close $ISSUE_NUM"
fi
Manual Issue Closure (If Needed)
If the issue wasn't auto-closed:
Ask the user first before doing this:
ISSUE_NUM=<issue_number>
PR_NUM=<pr_number>
# Close with reference to PR
gh issue close "$ISSUE_NUM" --comment "Closed via PR #$PR_NUM"
Cleanup: Remove Worktree
After PR merge, clean up the worktree:
# Switch to main worktree first
cd /workspace
# Remove the worktree
WORKTREE_PATH="wip/<branch-name>" # e.g., wip/i42-add-auth or wip/m8-p123-i234-add-auth
git worktree remove "$WORKTREE_PATH"
# Prune stale references
git worktree prune --verbose
Cleanup: Delete Branch
Delete local and remote branches:
BRANCH="<branch_name>" # e.g., i42-add-auth or m8-p123-i234-add-auth
# Delete local branch
git branch -d "$BRANCH"
# If not fully merged, force delete
# git branch -D "$BRANCH"
# Delete remote branch (GitHub usually does this automatically)
git push origin --delete "$BRANCH" 2>/dev/null || echo "Remote branch already deleted"
Cleanup: Ensure /workspace is up to date
/workspaceshould be kept up to date with origin main- after merging,
cd /workspacethen ensure workspace is refreshed from origin main- in the unlikely case that another agent has put WIP in
/workspace(instead ofwip/<feature branch>) that would be affected by this action, ask user what to do
- in the unlikely case that another agent has put WIP in
Complete Cleanup Workflow
# Variables
ISSUE_NUM=225
BRANCH="claude/225-pr-lifecycle-skill"
WORKTREE_PATH="wip/claude-225-pr-lifecycle-skill"
# 1. Verify issue closed
gh issue view "$ISSUE_NUM" --json state -q '.state'
# 2. Switch to main worktree
cd /workspace
# 3. Pull latest changes
git checkout main
git pull origin main
# 4. Remove worktree
git worktree remove "$WORKTREE_PATH"
# 5. Delete local branch
git branch -d "$BRANCH"
# 6. Prune
git worktree prune --verbose
# 7. Verify cleanup - should only show main worktree
git worktree list
# Show only non-main branches (should be empty after cleanup)
git branch | grep -v "^\*" | grep -v "main\|master"
Quick Reference
Pre-PR Checklist
| Check | Command | Expected |
|---|---|---|
| In worktree | pwd | grep wip/ |
In wip/ directory |
| Branch format | git branch --show-current |
user/num-slug |
| Issue open | gh issue view N --json state |
OPEN |
| Has "Fixes #" | git log | grep -i "fixes #" |
Found keyword |
GitHub Closing Keywords
Any of these in PR body will auto-close the linked issue:
close,closes,closedfix,fixes,fixedresolve,resolves,resolved
Format: Fixes #123 or Fixes owner/repo#123
PR Title Emojis
| Emoji | Type | Triggers |
|---|---|---|
| โจ | feat | MINOR bump |
| ๐ฉน | fix | PATCH bump |
| ๐จ | BREAKING | MAJOR bump |
| ๐ | docs | No bump |
| โป๏ธ | refactor | No bump |
| ๐งช | test | No bump |
| ๐ง | build | No bump |
| ๐ | release | N/A |
Common API Commands
# Get owner/repo for API calls
REPO=$(gh repo view --json nameWithOwner -q .nameWithOwner)
# View issue
gh issue view <num> --json number,title,state
# View PR
gh pr view --json number,title,state,closingIssuesReferences
# PR comments (for reviews) - use --paginate to get ALL comments
gh api repos/$REPO/pulls/<num>/comments --paginate
# Reply to comment
gh api repos/$REPO/pulls/<num>/comments/<id>/replies -f body="message"
# Close issue manually
gh issue close <num> --comment "Closed via PR #N"
Integration with Other Skills
With git-worktrees Skill
Use git-worktrees skill to create proper worktrees before starting work:
# Create worktree for new issue
git worktree add -b alice/42-new-feature wip/alice-42-new-feature main
With gh-sub-issue Skill
For issues with parent-child relationships, verify PR targets:
# Check if issue has parent
gh sub-issue list 42 --relation parent
# If parent exists, PR should target parent branch, not main
Troubleshooting
"Issue not closed after merge"
Cause: Missing "Fixes #N" keyword in PR body.
Solution:
# Close manually
gh issue close <num> --comment "Closed via PR #<pr_num>"
# Prevent future issues: always include "Fixes #N" in PR body
"Branch name doesn't match issue"
Cause: Branch created without following convention.
Solution: Rename branch before PR:
git branch -m old-name i42-proper-name
git push origin -u i42-proper-name
git push origin --delete old-name
"Can't determine issue number"
Cause: Branch name doesn't contain issue number.
Solution: Check branch name, rename if needed:
git branch --show-current
# If no number, rename branch to include issue number
"PR shows wrong closing issues"
Cause: Wrong issue number in PR body.
Solution: Edit PR body on GitHub or:
gh pr edit <num> --body "$(cat updated-body.md)"
See Also
- git-worktrees skill - Create and manage worktrees for issue branches
- gh-sub-issue skill - Manage parent-child issue relationships
- GitHub Workflow Documentation -
@docs/dev/github-workflow.md - Git SCM Conventions -
@docs/dev/git-scm-conventions.md - GitHub Keywords Docs - https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue