| name | Dev10x:gh-pr-triage |
| description | Validate a PR review comment against the codebase. If invalid, reply with evidence. Never auto-resolves threads — resolution requires explicit user confirmation. Returns a verdict (VALID, YAGNI, INVALID, QUESTION, OUT_OF_SCOPE) so the caller knows whether to fix, remove out-of-scope code, reply, or defer. TRIGGER when: PR review comment needs validation before implementing fix. DO NOT TRIGGER when: comment is clearly valid and needs immediate fix (use Dev10x:gh-pr-fixup directly).
|
| user-invocable | true |
| invocation-name | Dev10x:gh-pr-triage |
| allowed-tools | ["mcp__plugin_Dev10x_cli__pr_comment_reply","mcp__plugin_Dev10x_cli__pr_comments","mcp__plugin_Dev10x_cli__pr_issue_comment","Bash(gh api:*)"] |
Triage PR Review Comment
Overview
This skill evaluates whether a PR review comment requires a code change by
investigating the codebase. It replies to non-valid comments with evidence but
never auto-resolves threads. Returns a verdict to the caller.
Verdicts:
| Verdict | Meaning | Action taken |
|---|
VALID | Comment identifies a real issue in code that belongs in this PR | None — caller fixes |
YAGNI | Real issue, but the commented code is out-of-scope for the PR's JTBD — fix is to remove or defer the code, not harden it | Reply naming the scope mismatch; caller routes to removal |
INVALID | Comment is factually wrong (code already correct) | Reply with evidence |
QUESTION | Reviewer asking a question, no code change needed | Reply with answer |
OUT_OF_SCOPE | Valid concern, but the fix would expand the PR's scope (code itself is in scope) | Acknowledge, defer concern to follow-up |
YAGNI vs OUT_OF_SCOPE — when to use each:
OUT_OF_SCOPE — The commented code is legitimately part of this PR.
The reviewer asks for an enhancement, refactor, or stricter handling
that would expand the change beyond its stated JTBD. Defer the concern
to a follow-up ticket; the code stays.
YAGNI — The commented code itself does NOT belong in this PR. The
reviewer's bug report is correct, but the right fix is to remove or
revert the code rather than harden it. Multiple YAGNI verdicts that
share one root feature should bundle into a single removal commit
(see § Step 4.5).
Thread resolution policy: Never auto-resolve threads. Thread
resolution requires explicit user confirmation. The user supervising
the PR review needs resolved threads to remain visible so they can
verify the triage decisions without searching through hidden threads.
When to use this skill:
- Called by
Dev10x:gh-pr-respond before delegating to Dev10x:gh-pr-fixup
- Standalone when you want to validate a comment without committing to a fix
NEVER inline triage (GH-463, GH-97)
This is a Skill()-only entry point. The verdicts above
(VALID/YAGNI/INVALID/QUESTION/OUT_OF_SCOPE) are this
skill's output contract — they MUST NOT appear in narrative text
as a substitute for invoking the skill.
If you find yourself writing:
"Comment 3 is VALID — the test does need a fixture."
without a preceding Skill(Dev10x:gh-pr-triage) tool call, you
have bypassed the delegation. The bypass typically happens under
context pressure when the verdict feels obvious. It is exactly
the regression GH-463 and GH-97 caught (~42% compliance in the
audited session) — the agent classified five comments inline
across one PR cycle, all without a single Skill() call. The
verdict feeling obvious is precisely the rationalization that
triggers the bypass.
Detection hint for parent skills: When Dev10x:gh-pr-respond
or another caller observes inline classification language
without a preceding Skill(Dev10x:gh-pr-triage) call in the
same turn, the caller MUST abort the current comment and
re-enter via this skill. See Dev10x:gh-pr-respond/instructions.md
§ Critical: Delegation is Mandatory.
Orchestration
This skill follows references/task-orchestration.md patterns.
Auto-advance: Complete each step, immediately start the next — no checkpoints under adaptive friction.
Never pause to ask "should I continue?" between steps.
REQUIRED: Create tasks before ANY work. Execute these
TaskCreate calls at startup:
TaskCreate(subject="Fetch comment context", activeForm="Fetching comment context")
TaskCreate(subject="Validate against codebase", activeForm="Validating against codebase")
TaskCreate(subject="Draft verdict and reply", activeForm="Drafting verdict")
Set dependencies: validate blocked by fetch, draft blocked by
validate.
Input Requirements
- PR URL or Comment URL — Full GitHub URL to the PR comment
(e.g.,
https://github.com/owner/repo/pull/123#discussion_r456)
- Repository — Owner/repo (extracted from URL or defaults to current repo)
Optional additional context:
- User may provide extra context after the URL
- Example:
/Dev10x:gh-pr-triage https://...#discussion_r456 this is a Django project
Workflow
Step 1: Parse Input and Fetch Comment
Parse the comment URL:
URL format: https://github.com/{owner}/{repo}/pull/{pr_number}#discussion_r{comment_id}
Fetch the comment:
mcp__plugin_Dev10x_cli__pr_comments(action="get", comment_id={comment_id})
Extract:
body — The comment text
path — File path the comment is on
line / original_line — Line number in the diff
diff_hunk — Code context around the comment
html_url — Direct link
user.login — Who left the comment
in_reply_to_id — Parent comment (null for root comments)
reactions — Emoji reaction counts (+1, -1, laugh, hooray,
confused, heart, rocket, eyes, total_count)
Reaction signal: When reactions.total_count > 0, record which
reactions the PR author (or other reviewers) left and apply a lean:
| Reactions present | Lean | Meaning |
|---|
+1 and/or heart and/or rocket | VALID lean | Maintainer approves the suggestion |
-1 and/or confused | INVALID/decline lean | Maintainer rejects the suggestion |
eyes only | No lean | Maintainer is watching, no stance yet |
Mixed (+1 + -1) | No lean | Conflicting signals, fall through |
None (total_count == 0) | No lean | No reaction signal |
The lean is advisory — it overrides investigation only when no
directing prose exists in the comment body (body is empty or contains
only a code suggestion block with no explanatory text). When prose is
present, treat the lean as corroborating evidence but always investigate.
When applying a reaction lean, surface an inferred rationale for
confirmation, e.g.:
- VALID lean: "Applying — maintainer 👍 this suggestion (no prose verdict)"
- INVALID lean: "Declining — maintainer 👎 this suggestion. Inferred
rationale: inconsistent with sibling pattern / out of scope.
Confirm or override?"
Step 2: Fetch All PR Threads
Check for previously addressed issues to avoid duplicate work:
mcp__plugin_Dev10x_cli__pr_comments(action="list", pr_number={pr_number})
Look for:
- Threads already resolved
- Threads with fixup commit references in replies
- Threads raising the same concern as this comment
Step 3: Classify the Comment
Reaction lean check: Before classifying by prose, check whether
a reaction lean was recorded in Step 1 and whether the comment body
lacks directing prose (empty body or pure code-suggestion block with
no explanatory text). If both conditions hold, short-circuit:
- VALID lean → proceed directly to Step 5 (VALID verdict) with
reaction_signal: true. Surface the inferred rationale for
confirmation: "Applying — maintainer 👍 this suggestion".
- INVALID/decline lean → proceed directly to Step 5 (INVALID
verdict) with
reaction_signal: true. Surface the inferred
rationale: "Declining — maintainer 👎 this suggestion. Inferred
rationale: [best-match reason from diff_hunk context]. Confirm
or override?"
If prose is present (or no reaction lean), classify normally:
| Type | Signals |
|---|
| Code suggestion | "should use X", "change to Y", suggestion block |
| Missing-feature claim | "missing field/method/annotation" |
| Pattern concern | "should follow pattern X", "inconsistent with Y" |
| Question | Ends with ?, starts with "why", "how", "what" |
| Scope expansion | "also consider", "might want to add", "future" |
| Intentional design question | "why not use X?", "why different from Y?" |
Step 4: Investigate Using Validation Patterns
Load references/validation-patterns.md for the full catalog. Common
investigations:
Inherited Field — Reviewer claims a field/method is missing:
1. Read the file at the commented line
2. Find the class definition
3. Trace inheritance chain (check base classes, mixins)
4. If field/method exists in a parent → INVALID
Existing Convention — "Should use X instead of Y":
1. Grep codebase for both patterns X and Y
2. Count occurrences of each
3. If Y is the established pattern → INVALID
4. If X is more common → VALID (or QUESTION if close)
Already Present — "Missing type/annotation/test":
1. Read exact file + line range
2. Check if the claimed missing thing is already there
3. If present → INVALID with exact line reference
Established Sibling — "Change signature/return type":
1. Find all sibling implementations (same interface/pattern)
2. List their signatures
3. If current code matches siblings → INVALID
Previously Addressed — Same concern raised in other threads:
1. Search all PR threads for similar keywords
2. Check if a fixup commit already addressed it
3. If addressed → INVALID with thread URL reference
Step 4.5: JTBD Scope Check (GH-297)
Before rendering a final VALID verdict, check whether the commented
code is in-scope for the PR's stated Job Story. A correct bug report
against code that doesn't belong in the PR should route to YAGNI
(remove or defer the code), not VALID (harden it).
Trigger this check only when the investigation in Step 4 concludes
the comment is factually correct (about to verdict VALID). Skip for
INVALID, QUESTION, or pre-existing OUT_OF_SCOPE paths.
Procedure:
- Extract the PR's stated JTBD — read the PR body's Job Story
block. If absent, fall back to the PR title's outcome phrase
("Enable …", "Fix …", "Refactor …").
- Locate the commented code's origin in this PR — was the
commented line added or modified by this PR, or is it
pre-existing untouched code? (Check
git blame against the PR's
merge-base or use the comment's diff_hunk context.)
- Compare the commented code's purpose against the JTBD:
- Does the commented code implement the JTBD outcome?
- Or is it incidental scope-creep (a speculative feature, an
opportunistic refactor, a "while I'm here" addition) riding on
a PR scoped to something else?
- Cross-thread bundling signal — scan other PR threads (from
Step 2) for comments targeting the same speculative code path.
When two or more correct bug reports cluster on one out-of-scope
feature, all of them route to YAGNI with a shared removal
recommendation. Record the bundle in the verdict output so
Dev10x:gh-pr-respond can collapse them into one removal commit.
Decision matrix:
| Code is in-scope for JTBD? | Comment correct? | Verdict |
|---|
| Yes | Yes | VALID |
| No (speculative / drift) | Yes | YAGNI |
| Yes | Reviewer asks more | OUT_OF_SCOPE (defer concern) |
When in doubt, default to VALID. YAGNI is the explicit carve-out
for cases where the scope mismatch is clearly evident from the JTBD
and the diff context. If the JTBD is vague or the code's relationship
to it is ambiguous, choose VALID and let the author/reviewer decide
removal vs hardening during fixup.
Step 5: Render Verdict
Based on investigation, choose one of:
VALID — Real issue, needs a fix
Do nothing. Return verdict to caller (usually Dev10x:gh-pr-respond) which will
delegate to Dev10x:gh-pr-fixup.
Output:
Verdict: VALID
Reason: {brief explanation of why the comment is correct}
Signal: text | reaction:👍 | reaction:❤️ | reaction:🚀
YAGNI — Real issue, but code is out-of-scope for the PR's JTBD
Post a brief reply naming the scope mismatch and proposing removal.
Do NOT resolve the thread — the caller (Dev10x:gh-pr-respond)
collapses related YAGNI verdicts into a single removal commit and
closes the threads together.
Reply format:
Correct catch — {one-sentence acknowledgment of the bug}.
This code is out of scope for the PR's JTBD ({JTBD outcome phrase}).
The right fix is to remove {speculative feature / drifted code} from
this PR rather than harden it. Will bundle with related thread(s) into
a single removal commit, or defer to a follow-up ticket if the feature
is desired.
Output:
Verdict: YAGNI
Reason: {scope-mismatch explanation, naming the JTBD and the out-of-scope code}
Bundle: {comma-separated comment IDs of related YAGNI threads, or "single"}
Action: Replied acknowledging removal route (thread left open for user to resolve)
INVALID — Comment is factually wrong
Post an evidence-based reply. Do NOT resolve the thread.
Reply format:
{evidence explanation}
{code snippet or grep results showing the evidence}
Output:
Verdict: INVALID
Reason: {brief explanation}
Action: Replied with evidence (thread left open for user to resolve)
Signal: text | reaction:👎 | reaction:😕
QUESTION — No code change needed
Post a clear answer explaining the design decision or code behavior.
Do NOT resolve the thread.
Reply format:
{answer to the question with context}
Output:
Verdict: QUESTION
Reason: {brief explanation}
Action: Replied with answer (thread left open for user to resolve)
OUT_OF_SCOPE — Valid but beyond this PR
Post a brief acknowledgment. Do NOT resolve the thread.
Reply format:
Out of scope for this PR. {optional: brief reason or link to tracking ticket}
Output:
Verdict: OUT_OF_SCOPE
Reason: {brief explanation}
Action: Acknowledged (thread left open for user to resolve)
Reply Mechanics
Post reply in the thread (not top-level) via the MCP tool:
mcp__plugin_Dev10x_cli__pr_comment_reply(
pr_number=<int>,
comment_id=<int>,
body="<reply_text>",
repo="<owner>/<repo>",
)
The tool posts to /pulls/{pr_number}/comments with
in_reply_to=<comment_id> and coerces numeric-string IDs to
int defensively (GitHub rejects strings as in_reply_to).
Fallback (only when the MCP server is unavailable):
gh api \
--method POST \
repos/{owner}/{repo}/pulls/{pr_number}/comments/{comment_id}/replies \
-f body="{reply_text}"
Top-level / body finding replies: If the triaged comment is a
top-level PR comment (URL contains #issuecomment-, posted via
gh pr comment) or a review-body finding without a thread, the
review-thread reply mechanism above returns 404. Use the
issue-level MCP tool instead:
mcp__plugin_Dev10x_cli__pr_issue_comment(
pr_number=<int>,
body="<reply_text>",
repo="<owner>/<repo>",
)
This wraps POST /repos/{owner}/{repo}/issues/{pr_number}/comments
and is the same channel claude[bot] uses to post top-level
findings — replies posted through it appear inline with the
original finding in the PR conversation.
Thread resolution: Do NOT resolve threads. Return the verdict to the
caller (Dev10x:gh-pr-respond or the user). Resolution only happens when the user
explicitly confirms it — either via Dev10x:gh-pr-respond's confirmation flow or
by direct user request.
Error Handling
Comment Not Found
Error: Could not fetch comment {comment_id}.
Verify the comment ID and repository.
Ambiguous Verdict
If investigation is inconclusive:
- Default to
VALID (let the fix author decide)
- Log uncertainty: "Leaning VALID — investigation inconclusive"
- Never default to
YAGNI — it requires an explicit JTBD-scope
mismatch (Step 4.5). Ambiguous scope means VALID and let the
author choose removal vs hardening at fixup time.
Thread Already Resolved
If the thread is already resolved, skip it:
Skipped: Thread already resolved
Integration
Dev10x:gh-pr-monitor → Dev10x:gh-pr-respond (orchestrator)
├── Dev10x:gh-pr-triage ← this skill
└── Dev10x:gh-pr-fixup
└── commit:fixup
Standalone usage:
/Dev10x:gh-pr-triage https://github.com/owner/repo/pull/123#discussion_r456
Called by Dev10x:gh-pr-respond:
Dev10x:gh-pr-respond receives comment URL
→ delegates to Dev10x:gh-pr-triage
→ if VALID → delegates to Dev10x:gh-pr-fixup
→ if INVALID/QUESTION/OUT_OF_SCOPE → Dev10x:gh-pr-triage replied,
Dev10x:gh-pr-respond asks user to confirm thread resolution
References
references/validation-patterns.md
Contains the full catalog of validation patterns with detection heuristics
and investigation steps.
references/github_api.md
Contains GitHub API documentation for:
- Listing PR comments
- Fetching single comments
- Creating replies
- Resolving review threads (GraphQL)
- Filtering and querying