with one click
review-pr
Review a PR for correctness, pattern compliance, testing, accessibility, and safety. Produces a confidence score for merge readiness.
Menu
Review a PR for correctness, pattern compliance, testing, accessibility, and safety. Produces a confidence score for merge readiness.
| name | review-pr |
| description | Review a PR for correctness, pattern compliance, testing, accessibility, and safety. Produces a confidence score for merge readiness. |
| argument-hint | <PR-number-or-branch> |
| allowed-tools | Bash Read Grep Glob |
Review PR $ARGUMENTS and produce a confidence score for merge readiness.
# PR metadata
gh pr view $ARGUMENTS --json title,body,author,labels,files,additions,deletions,baseRefName,headRefName,state,isDraft,number
# Changed files list
gh pr diff $ARGUMENTS --name-only
# Full diff
gh pr diff $ARGUMENTS
# CI status
gh pr checks $ARGUMENTS
Determine the PR type from changed files and metadata:
| Type | Detection | Check scope |
|---|---|---|
| docs-only | All files are *.md, docs/**, **/stories/**, **/.storybook/** | Change file only |
| test-only | All files are *.test.*, *.spec.*, **/testing/** | Change file + test quality |
| bug-fix | Branch starts with fix/ or title contains "fix" | All checks, extra weight on tests |
| feature | Branch starts with feat/ or adds new exports | All checks, extra weight on API + patterns |
| refactor | No new exports, restructures existing code | All checks, extra weight on no behavior change |
| config/infra | Changes to CI, configs, scripts only | Change file + no regressions |
For v8 packages (packages/react/): skip V9 pattern checks — those are maintenance-only with different patterns.
For web-components (packages/web-components/): skip React-specific checks.
Run each check category. For each finding, assign a severity:
Required if any published package source code changed (not just tests/stories/docs).
change/ directory in the diff for new .json filespatch for fixes, minor for features, never major without explicit approvalBLOCKER if missing for published source changes.
Only for files in packages/react-components/react-*/library/src/:
| Check | Look for | Severity |
|---|---|---|
No React.FC | React.FC, : FC<, React.FunctionComponent in added lines | BLOCKER |
| No hardcoded styles | Hex colors #[0-9a-fA-F]{3,8}, hardcoded px values for spacing/radius/font in .styles.ts files | WARNING |
| Griffel usage | Style files must use makeStyles from @griffel/react, not inline styles | WARNING |
| mergeClasses order | User className must be the LAST argument in mergeClasses() | WARNING |
| Slot system | New components must use slot.always/slot.optional and assertSlots | WARNING |
Reference: docs/architecture/component-patterns.md
For changes to package.json files or new imports in Tier 3 component packages:
react-button, react-menu, etc.) adds a dependency on another Tier 3 packagereact-utilities, react-theme, react-shared-contexts, react-tabster, react-positioning, react-portal@griffel/react, @fluentui/tokens, @fluentui/react-jsx-runtimeReference: docs/architecture/layers.md
Grep added lines for unguarded browser API access:
| Pattern | Severity |
|---|---|
window. without canUseDOM or typeof window guard nearby | BLOCKER |
document. without guard | BLOCKER |
navigator. without guard | BLOCKER |
localStorage / sessionStorage without guard | BLOCKER |
instanceof HTMLElement | WARNING |
Check 3 lines above each match for a guard (canUseDOM, typeof window !== 'undefined').
| Check | Severity |
|---|---|
Source files changed but no corresponding .test.tsx changes | WARNING |
New component missing testing/isConformant.ts | WARNING |
Snapshot files need updating (render/style changes without .snap updates) | INFO |
| Check | Severity |
|---|---|
Public API changed but etc/*.api.md not updated | WARNING |
| Existing exports removed (breaking change) | BLOCKER |
| New exports added (flag for human review) | INFO |
| Check | Severity |
|---|---|
Existing aria-* attributes removed | BLOCKER |
onClick without onKeyDown/onKeyUp handler | WARNING |
Interactive elements missing role or aria-* | WARNING |
Images/icons without aria-label or aria-hidden | WARNING |
| Check | Severity |
|---|---|
eval() or new Function() | BLOCKER |
dangerouslySetInnerHTML | WARNING |
console.log / debugger in production code | WARNING |
// @ts-ignore without explanation | WARNING |
any type in new code | INFO |
A code change frequently lands something that someone downstream needs to learn about. The "someone" splits into two audiences with very different reading habits, so this check actually walks two passes:
AGENTS.md, docs/workflows/*, and docs/architecture/*. When a PR changes a build target, a script, a CI step, a label taxonomy, a project-board field, or an assumption that a skill makes, the skill or harness doc has to be updated in the same PR — otherwise every fresh /triage-issues, /visual-test, /review-pr, etc. invocation pays the discovery cost over again.Both passes ask the same two questions: should this PR have updated docs, and if so, did it? Bug fixes that restore documented behavior, internal refactors, and test-only PRs default to PASS in both passes — the interesting cases are below.
| Source change | Expected doc surface |
|---|---|
New prop on a public component (added in *.types.ts) | A Storybook story or MDX entry under packages/react-components/react-<name>/stories/ that exercises the prop |
New public export added to a library/src/index.ts barrel | A story or MDX entry covering it (or a justified note in the PR body) |
New component package (new directory under packages/react-components/react-*) | Stories package, library/docs/Spec.md, and an entry on the docsite (apps/public-docsite-v9) |
Behavior change to a public API's defaults (e.g. a new conditional in use*_unstable that changes observable output) | Mention in library/docs/MIGRATION.md or a "BREAKING CHANGE / behavior change" section in the PR body |
| Removal or deprecation of a public export | library/docs/MIGRATION.md entry and/or a @deprecated JSDoc on the symbol |
| New design token, classname constant, or CSS custom property | A line in the relevant docs (often the component's stories MDX) explaining how to use/override it |
This pass exists because skills are part of the contract, not just convenience. When the skills are stale, the agents are wrong — quietly. Symptoms: a /visual-test invocation that points at a nonexistent project name, a /triage-issues flow that recommends a label the repo no longer has, a /triage-board skill that misses a new view filter the maintainers added.
Walk the diff for any of these and check whether the matching agent-facing doc was updated:
| Source change | Expected harness update |
|---|---|
New / renamed nx target (project.json, nx.json, generators) | Any skill that invokes that target — visual-test, lint-check, package-info, v9-component. The skill literally types the target name; it's not auto-discovered. |
New / renamed top-level package.json script | Same. Plus docs/workflows/contributing.md if it's contributor-facing. |
New label, label rename, or label taxonomy change in the repo (gh api repos/.../labels) | triage-issues/references/triage-labels.md — the skill uses an allow-list and gh issue edit rejects unknown labels. |
| New / changed project-board field, view filter, or option ID | triage-board/references/team-mapping.md (option IDs) and the view-filter mirror in triage-board/SKILL.md. |
| New convention contributors are expected to follow (file layout, naming, "always do X") | AGENTS.md / CLAUDE.md (they're symlinked) — the rules section is the agent's first read. |
| New layered dependency rule, package-tier reshuffle, or CODEOWNERS rewrite | docs/architecture/layers.md, docs/team-routing.md, plus triage-board/references/team-mapping.md if a CODEOWNERS handle gained or lost a confident mapping. |
New first-time-setup requirement (e.g. a workspace package whose lib-commonjs/ must be pre-built) | docs/workflows/contributing.md "First-time setup" section AND the troubleshooting block of any skill that hits the failure mode (visual-test already does this for the unstable-deps case). |
New skill, agent hook, or .claude/-level configuration | AGENTS.md skills table + the .claude/skills/<name>/SKILL.md bridge file. |
| Removal or move of a path that any skill greps for or hard-codes | The skill that referenced it. Search the skills (grep -rn "<old-path>" .agents/skills/) before merging. |
Authentication / token-scope / permission changes that affect gh calls | The skill's preflight section. Concrete past examples: the EMU vs non-EMU active-account gotcha and the read:project vs project scope distinction now baked into triage-board's preflight. |
| New external-tool name or behavior change (e.g. assignee identity for an automated agent) | The relevant skill. Concrete past example: assigning to Copilot literally fails with "Bot does not have access" — only copilot-swe-agent works. That belongs in any skill that assigns to Copilot. |
| Situation | Severity |
|---|---|
Public export removed or behavior-changed without MIGRATION.md entry | BLOCKER |
| Skill references a renamed or removed path / target / label that this PR changes (skill will literally fail next run) | BLOCKER |
| New public component / new public hook with no story or MDX | WARNING |
| New public prop with no story exercising it | WARNING |
| New design token / className constant with no usage example | WARNING |
| Behavior change to public defaults with no PR-body callout | WARNING |
New label / project field / nx target / convention without the matching skill or AGENTS.md / docs/workflows/ update | WARNING |
New first-time-setup requirement without contributing.md mention | WARNING |
| PR description explicitly defers docs to a follow-up (link cited) | INFO |
| Docs / harness change exists but feels minimal — could be more thorough | INFO |
The model should walk the diff, not guess.
library/src/**, index.ts barrels, *.types.ts); user-doc surfaces (stories/**, library/docs/**, apps/public-docsite-v9/**); harness surfaces (.agents/skills/**, .claude/**, AGENTS.md, CLAUDE.md, docs/workflows/**, docs/architecture/**, docs/team-routing.md, project.json, nx.json, root package.json scripts, .github/CODEOWNERS, .github/labeler.yml).grep -rn "<old-name>" .agents/skills/ docs/ AGENTS.md to find references that will break. Anything that turns up is a BLOCKER unless this PR also updates it.Start at 100
For each BLOCKER: -25 points
For each WARNING: -5 points
For each INFO: -1 point
Bonuses:
+5 if tests added/updated alongside source changes
+3 if change file present and well-described
+2 if PR description is thorough
Floor at 0, cap at 100.
Score interpretation:
Use this exact format (also used verbatim as the PR-comment body in Phase 6 — don't duplicate work).
Start directly with the score. Skip a header title, author, type, packages-affected, and CI-status preamble — when this is posted as a PR comment, all of that is already visible in the GitHub UI immediately above the comment, so repeating it just pushes the actually-useful content (score + findings) below the fold. The classification work from Phase 2 still happens; it just isn't echoed back at the reader.
### Confidence Score: <score>/100
<one-sentence summary>
### Findings
#### Blockers (must fix before merge)
- [ ] <finding with file:line reference>
#### Warnings (should address)
- [ ] <finding with file:line reference>
#### Info (consider)
- <finding>
### Category Breakdown
| Category | Status | Notes |
|----------|--------|-------|
| Change file | PASS/FAIL | ... |
| V9 patterns | PASS/WARN | ... |
| Dep layers | PASS/FAIL | ... |
| SSR safety | PASS/WARN | ... |
| Testing | PASS/WARN | ... |
| API surface | PASS/WARN | ... |
| Accessibility | PASS/WARN | ... |
| Security/Quality | PASS/WARN | ... |
| Docs coverage | PASS/WARN/FAIL | ... |
### Recommendation
APPROVE / REQUEST_CHANGES / COMMENT
<brief rationale>
After presenting the output in the chat, post the same text as a comment on the PR so the review is visible to maintainers and to Copilot (when the PR author is copilot-swe-agent, the comment becomes actionable feedback).
Save the output from Phase 5 to a temp file so the markdown isn't mangled by shell quoting, then:
gh pr comment $ARGUMENTS --repo microsoft/fluentui --body-file /tmp/pr-review-$ARGUMENTS.md
Append a single trailer line to the body so the post is identifiable:
---
*Posted via the `/review-pr` skill.*
The posted comment should be identical to what you rendered in chat — don't paraphrase or summarize. The chat output and the PR comment must match so the user can trust that what they saw is what the maintainers see.
Pre-checks before posting:
gh account has write access to the PR's repo (EMU accounts read fine but silently fail on writes):
gh api graphql -f query='{ viewer { login } repository(owner:"microsoft", name:"fluentui") { viewerPermission } }'
If viewerPermission is NONE, stop and ask the user to gh auth switch --user <non-emu-account>.REQUEST_CHANGES is the recommendation, still post the comment but note to the user that only a formal review (gh pr review --request-changes) actually blocks merge; a comment is advisory.When to skip posting:
*Posted via the \/review-pr` skill.*` trailer.### Confidence Score: NN/100 line must always appear on its own line for machine parsing.Triage newly-filed GitHub issues on the Fluent UI repo (microsoft/fluentui) following the Shield triage guidelines. Fetches open issues labeled `Needs: Triage :mag:` via the `gh` CLI, classifies each (bug vs feature, product area, partner ask, repro quality, a11y), recommends label changes and area-owner assignment, and then applies the approved changes. Use this skill whenever the user asks to triage issues, run shield triage, go through the triage queue, process needs-triage, or any variation -- even if they don't mention "Fluent UI" or "GitHub" explicitly, since that's the project context here.
Visually verify a component by launching its Storybook story and taking a screenshot with playwright-cli. Use after making visual changes to a component.
Create a beachball change file for the current changes. Determines change type (patch/minor) and generates a description from the diff.
Run lint on affected packages, parse errors, and auto-fix common issues (design tokens, React.FC, SSR safety, import restrictions)
Quick lookup for a Fluent UI package — path, dependencies, owner team, Nx project details, and relevant docs
Find the matching Fluent UI design token for a hardcoded CSS value (color, spacing, font size, border radius, shadow)