From github-triage
Use when triaging GitHub issues or pull requests - categorizing, labeling, organizing open items, validating if issues are still relevant, and reviewing/merging PRs. Handles taxonomy discovery, label creation, bulk tagging, issue staleness checks, and full PR review + merge workflows.
npx claudepluginhub prime-radiant-inc/github-triage --plugin github-triageThis skill uses the workspace's default tool permissions.
Systematically categorize, validate, and act on open GitHub issues and PRs.
Mandates invoking relevant skills via tools before any response in coding sessions. Covers access, priorities, and adaptations for Claude Code, Copilot CLI, Gemini CLI.
Share bugs, ideas, or general feedback.
Systematically categorize, validate, and act on open GitHub issues and PRs.
Core principle: Discover what dimensions matter for THIS project, propose a taxonomy, get approval, then tag everything. Never apply labels you haven't verified exist. When reviewing PRs or validating issues, use parallel sub-agents for efficiency.
1. Label all issues and PRs (Phases 1-5)
2. Test whether open issues are still reproducible (Phase 6)
3. Review and merge open PRs (Phase 7)
Foundational framing — bug reports are not product defects. A bug report is a report of a possible problem by a reporter who is an unreliable narrator. It is not the same thing as a defect in the product. Reporters misdiagnose root causes. They attribute symptoms to the wrong code. They describe environment-specific behavior as universal. They confuse correlation with causation. They make claims that turn out not to be reproducible at all.
When you triage an issue, your job is not to verify the reporter's story. Your job is to determine whether the behavior they describe still occurs. The file the reporter pointed at being unchanged tells you nothing — they may have pointed at the wrong file, or the behavior may have a different cause, or the behavior may not exist anymore (or ever).
Treat every bug report as a hypothesis to be tested by reproduction, not a fact to be verified by inspection.
Pull all open issues and PRs in parallel. Scan titles, bodies, and comments for recurring patterns.
# Run these in parallel
gh issue list --repo OWNER/REPO --state open --json number,title,body,labels,createdAt --limit 500
gh pr list --repo OWNER/REPO --state open --json number,title,body,labels,isDraft --limit 500
gh label list --repo OWNER/REPO --json name,description,color --limit 100
Look for:
Present discovered dimensions and proposed label values. Match existing label style:
windows, bug), stay flatplatform:windows, type:bug), use prefixesPresent as a table including proposed per-item mappings:
Dimension: type → bug, enhancement, documentation
Dimension: area → area:docker, area:devcontainer-spec, area:testing, area:tooling
Dimension: priority → priority:high (bugs that block users)
Dimension: status → status:needs-review, status:needs-reproduction, status:blocked, status:stale
Dimension: quality → no-obvious-human-review, pr-template-rules-ignored (if repo has templates)
| # | Title | Labels |
|-----|------------------------------|---------------------------------|
| #21 | mountpoint is outside rootfs | bug, priority:high, area:docker |
| #12 | HTTPS tarball support | enhancement, area:devcontainer |
...
Showing the per-item mapping upfront lets the user approve taxonomy AND assignments in one step.
Do not proceed until user approves.
Create ONLY approved labels. Check each one first — gh issue edit --add-label silently auto-creates labels that don't exist, which pollutes the label namespace.
# Create only if missing
gh label create "label-name" --repo OWNER/REPO --description "description" --color "hex"
Never let gh issue edit --add-label auto-create labels.
Apply labels from the approved taxonomy. Batch all commands — no need to re-read between labeling.
gh issue edit NUMBER --repo OWNER/REPO --add-label "label1,label2"
gh pr edit NUMBER --repo OWNER/REPO --add-label "label1,label2"
Confidence rule: Apply needs-categorization when uncertain. A wrong label is worse.
Reproduction rule: Apply needs-reproduction to bug reports that lack clear steps to reproduce, have vague descriptions ("it doesn't work"), are missing version/environment info, or don't include error output. A bug that can't be reproduced can't be fixed.
AI authorship rule: Apply no-obvious-human-review (or repo equivalent) to issues and PRs that appear fully AI-generated with no meaningful human oversight. Signals to look for:
cci: URIs, session URLs, local file paths)Not all AI-assisted PRs warrant this label. Exclude submissions that show:
Template compliance rule: If the repo has issue or PR templates, check whether submissions filed after the template was introduced actually use it. Apply pr-template-rules-ignored (or repo equivalent) to PRs that either skip the template entirely or leave required sections blank/placeholder. This is an objective, mechanical check — apply it consistently regardless of content quality. A PR can have good content and still get this label if it ignored the template. To determine when templates were introduced, check git log for the template files (e.g., .github/PULL_REQUEST_TEMPLATE.md).
Skip closed issues unless explicitly asked.
## Triage Summary
Tagged: 16 issues, 6 PRs
| Label | Count |
|------------------------|-------|
| enhancement | 13 |
| bug | 7 |
| area:devcontainer-spec | 10 |
| area:docker | 9 |
| priority:high | 1 |
Go through open issues oldest-first. For each one, the question to answer is: "can this still be reproduced?" — not "is the file content the reporter described still there?"
# Get issues sorted oldest-first
gh issue list --repo OWNER/REPO --state open --json number,title,body,createdAt --limit 500 \
| python3 -c "import json,sys; issues=sorted(json.load(sys.stdin), key=lambda x: x['number']); ..."
This is the most common mistake in issue triage and the one that produces the most false-positive "still valid" verdicts.
| Wrong question | Right question |
|---|---|
| "Does the file the reporter pointed at still say what they said it said?" | "If I follow the reporter's steps, does the claimed behavior happen?" |
| "Did we ship a fix for this?" | "Can I still reproduce the bug?" |
| "Does the feature exist in the codebase?" | "Does the actual user need described in the report still go unmet?" |
These are not equivalent. The reporter's narration is a theory about the bug. The bug itself is the behavior. Verify the behavior, not the theory.
Deterministic claims (shell scripts, hooks, plugin manifests, config files):
LLM-steering claims (e.g., "this skill text causes the model to do X"):
Multi-step session/agent claims (e.g., "agent stops at task 5", "subagent skips review"):
Harness-specific claims (Windows hook fails, Codex token usage, Cursor crashes):
needs-repro-on-<harness> labelFeature-request claims:
For each issue, one of:
| Verdict | Meaning | Action |
|---|---|---|
| REPRODUCED | Followed steps, observed the behavior | Keep open; consider priority labels |
| NOT REPRODUCIBLE | Followed steps, behavior didn't occur. Reporter was wrong, or bug has been fixed in a way that file inspection wouldn't show | Close with reproduction notes |
| NEED-REPRO-ENV | Can't reproduce in this environment (Windows-only, Codex-only, etc.) | Keep open; tag with needs-repro-on-<harness> |
| NEED-MORE-INFO | Report lacks enough detail to attempt reproduction | Comment asking for repro steps; tag needs-reproduction |
| NOT-A-DEFECT | RFC, feature request, philosophical discussion, or duplicate | Keep open or close per project policy |
gh issue close NUMBER --repo OWNER/REPO --comment "I tried to reproduce this following the steps in the report. [Specifics of what you did and what you observed.] The behavior described doesn't occur in [version/environment tested]. Closing — please reopen with fresh repro steps if you can still hit this. — Claude [model], Claude Code [version], session [session-id]"
The close comment must describe what you actually did to test, not just "we couldn't repro." Naming your test gives the reporter the chance to push back if you tested wrong.
These are mistakes Claude has made before — do not repeat them:
dev that mentions this issue number, so it's fixed." Maybe. Verify the behavior is gone on dev, not just that a commit exists.<feature-name>, so the feature request is still valid." Maybe — but verify the user need is unmet, not just the keyword absent. A different skill may already cover it.If the project uses a dev/release branch model, distinguish:
origin/main, latest tag)origin/dev but still on mainFor NOT-REPRODUCIBLE-ON-DEV, do NOT just close — confirm with the maintainer how they prefer to handle "fixed pending release" (label, milestone, or close-with-citation). This is a common false-close vector: closing based on a dev commit message when the user reading main still hits the bug.
For each open PR, follow this sequence: security review → code review + local tests → merge. Never check out or run PR code before the security review completes.
for pr in 30 29 27; do
gh pr view $pr --repo OWNER/REPO --json mergeable,mergeStateStatus
done
Invoke the pr-security-review agent on every PR before touching local code. The agent has read-only tools (Read, Grep, Glob, WebFetch) and cannot be compromised by what it analyzes.
Provide the agent:
https://github.com/OWNER/REPO/pull/NUMBER.diff or via gh pr diff NUMBER)Security verdict gates:
| Verdict | Action |
|---|---|
| ✅ SAFE | Proceed to Step 3 |
| ⚠️ REVIEW NEEDED | Present findings to user. If the fix is clear and small (e.g. missing input validation), offer to apply it yourself and ask the user whether to fix-then-merge or bounce back to the author. Wait for explicit direction before touching local code. |
| 🚫 BLOCK | Do not check out or run code. Report to user and stop. |
Agent 1 — Code Review (Explore agent):
gh pr diff NUMBER)Agent 2 — Test Runner (general-purpose agent):
gh pr checkout NUMBER --repo OWNER/REPOmake lint (or project equivalent)make test (or project equivalent)git checkout main when doneWhy local? CI may be broken, rate-limited, or testing a stale base. Local runs catch issues CI misses and confirm the code works on the actual machine it will be merged from.
| Code Review | Tests | Action |
|---|---|---|
| Approve | Pass | Present findings to user. Ask: "Shall I merge?" |
| Approve-with-notes | Pass | Present findings + notes to user. Ask: "Shall I merge?" |
| Approve | Fail (pre-existing only) | Present findings to user, note pre-existing failures. Ask: "Shall I merge?" |
| Approve | Fail (new failures) | Do not merge, report to user |
| Request changes | Any | Do not merge, report to user |
Conflicts: If mergeStateStatus is DIRTY, do not merge. Report to user with description of what conflicts.
MANDATORY: Never merge autonomously. After code review and tests complete — even when all checks are green — Claude MUST stop and present findings to the user. State the PR number, the code review verdict, and the test results. Then ask explicitly: "Shall I merge PR #N?" Wait for an affirmative reply ("yes", "go ahead", "merge it", etc.) before running any merge command. This rule applies unconditionally, regardless of how clear-cut the result appears.
When a PR comes from a fork (i.e., gh pr view N --json headRepositoryOwner returns someone other than the repo owner), you cannot push lint fixes or other improvements to the PR branch.
Instead of gh pr merge --squash (which would merge the PR as-is without your fixes), do a manual squash:
# 1. Confirm it's a fork
gh pr view N --repo OWNER/REPO --json headRepositoryOwner -q '.headRepositoryOwner.login'
# 2. Fetch the PR branch locally
git fetch origin pull/N/head:pr-N
# 3. Pull all changed files onto main
git checkout main
git checkout pr-N -- file1.go file2.go ... # only the files changed by the PR
# 4. Apply any fixes you need (lint, etc.)
# 5. Commit as a squash (summarize all PR changes in one message)
git commit -m "fix: ... (#N)\n\nCo-authored-by: Author <Author@users.noreply.github.com>"
# 6. Push
git push origin main
# 7. Close the PR manually with a comment explaining what you did
gh pr close N --repo OWNER/REPO --comment "Merged manually as squash commit <SHA> with <fix applied on top>. ..."
Co-author attribution: Always include the PR author in the commit via Co-authored-by: so their contribution appears in the repo's contributor graph.
When code review identifies that new code lacks test coverage (e.g. a new function has no unit tests), do not just request changes from the author. Instead:
Only request changes from the author if the code itself has correctness issues, not just missing tests. Missing tests are your problem to fix, not a reason to block the PR.
When a PR adds a new function that's supposed to fire at runtime (a warning, a hook, a side effect), grep for callers across the codebase before merging. It's easy to add the function and forget to wire it up.
grep -rn "functionName" pkg/ cmd/ --include="*.go"
If the function is defined but never called outside its own file, flag it and wire it in yourself before merging.
Before extracting fixes from a stale PR, run make lint and make test on main first. Many fixes may already be on main from earlier work. This avoids duplicating effort and tells you exactly what's still outstanding.
When you have a meaningful architectural concern or an alternative approach worth considering:
Do NOT block the PR on an architectural alternative. It is a discussion, not a blocker. The original PR proceeds to the normal merge decision independently.
# 1. Get onto the PR branch
gh pr checkout <number>
# 2. Branch off it
git checkout -b prototype/<short-description>
# 3. Implement the alternative approach
# 4. Run the full test suite — it must pass before you push
make test # or project equivalent
# 5. Commit with a clear message explaining the approach
git commit -m "prototype: <description of alternative approach>"
# 6. Push the prototype branch
git push origin prototype/<short-description>
# 7. Return to main
git checkout main
# 8. Leave a comment on the PR with the branch link and tradeoff comparison
gh pr comment <number> --body "..."
Tradeoff table format:
| Dimension | Current approach | Prototype approach |
|------------------|------------------|--------------------|
| Mount count | ... | ... |
| Complexity | ... | ... |
| Scope of change | ... | ... |
| Future-proofing | ... | ... |
When merging more than one PR in sequence, the second PR's branch was based on the old main — not the main that includes the first PR's changes. After merging PR A, you must rebase PR B's branch before merging it:
git checkout pr-B-branch
git rebase main # rebase onto main that now includes PR A
# resolve any conflicts
npm run build # or project equivalent — rebuild generated files
git checkout main
git merge --squash pr-B-branch
Conflicts from sequential merges are almost always in generated files (bundled dist, lock files, source maps). Resolve them by accepting the current main's version and rebuilding — never try to hand-merge a minified bundle.
gh pr merge NUMBER --repo OWNER/REPO --squash
git pull origin main
Always leave a comment thanking the author and identifying yourself:
gh pr comment NUMBER --repo OWNER/REPO --body "Thanks @author! [1-2 sentence summary of what the PR does and why it's good].
— Claude [model], Claude Code [version], session [session-id]"
Identification format: Always include:
Claude Opus 4.6)Claude Code 2.1.56)To get version and session:
claude --version # e.g., "2.1.56 (Claude Code)"
# Session ID is passed as --session-id to the Claude process; read it from the parent:
ps -p $PPID -o args= | grep -oE -- '--session-id [^ ]+' | awk '{print $2}'
$CLAUDE_SESSION_ID is not reliably set as an environment variable. Use the ps command above instead. If it returns empty (Claude was launched without an explicit session ID), omit the session ID from the comment rather than leaving a blank placeholder.
| Mistake | Fix |
|---|---|
| Labeling without checking existing labels | Always run gh label list first |
Letting gh issue edit auto-create labels | Create labels explicitly in Phase 3 |
| Confusing similar tool names | Read full issue body, not just title |
| Tagging closed issues | Skip unless explicitly asked |
| Applying labels with low confidence | Use needs-categorization instead |
| Skipping taxonomy approval | Always get approval before tagging |
| Closing an issue based on partial evidence | Grep for specific function/struct names, don't guess |
| Treating a bug report as a product defect | Bug reports are reports by unreliable narrators — they may have misdiagnosed. Validate by reproducing the behavior, not by checking whether the file content the reporter described still exists |
| Marking VALID because the cited line is unchanged | Existence of the code the reporter blamed ≠ existence of the bug. Run the scenario. The reporter may have pointed at the wrong file or misread the cause |
| Closing because a dev-branch commit mentions the issue number | Verify the behavior is actually gone on that branch — commit messages can lie or refer to a different aspect of the same bug |
| Marking feature-request VALID because keyword grep is empty | Search for synonyms; the feature may exist under a different name. Validate the user need is unmet, not just the keyword absent |
| Treating a behavior claim as testable by file inspection | LLM-steering and multi-step-session claims need real reproduction. If you can't run the session, mark UNCERTAIN — don't trust narration as evidence |
| Checking out PR code before security review | Always run pr-security-review agent first — it has read-only tools for safety |
| Merging without testing | Always run code review + test agents in parallel first |
| Merging without user confirmation | NEVER merge autonomously. Always present findings and ask "Shall I merge PR #N?" — wait for an explicit yes before running any merge command, even when all checks are green |
| Merging conflicting PRs | Check mergeStateStatus — never merge DIRTY |
| Missing identification in PR comments | Always include model, Claude Code version, session ID |
| Reviewing PRs sequentially | Launch code review and test agents in parallel |
| Thanking with wrong session ID | Get it via ps -p $PPID -o args= | grep -oE -- '--session-id [^ ]+' | awk '{print $2}'; omit if empty |
| Requesting changes for missing tests only | Write the tests yourself on the PR branch and push them |
| Leaving an architectural alternative as a comment only | Build a prototype branch, run tests, push it, then comment with branch link + tradeoff table |
| Blocking a PR on an architectural discussion | Prototype discussions are non-blocking; PR proceeds to normal merge decision |
| Pushing lint fixes to a fork PR branch | You can't — detect with headRepositoryOwner, then use manual squash pattern |
| Merging second PR without rebasing after first landed | Always rebase each PR branch onto current main before merging; conflicts will be in generated files — resolve by rebuilding |
| Merging a PR that adds a function never called | Grep for callers; if none exist outside the file, wire it up before merging |
| Duplicating work from a stale PR | Run lint/tests on main first to see what's actually still outstanding |
| Labeling all AI-assisted PRs as no-human-review | Only flag submissions with NO evidence of human involvement; AI-assisted PRs with real evaluation, honest limitations, or human voice are fine |
| Skipping template compliance check | If repo has templates, check git log for when they were added; tag all post-template PRs that ignored them, regardless of content quality |
| Guessing when templates were introduced | Always verify with git log -- .github/PULL_REQUEST_TEMPLATE.md (or equivalent path) |