Review a PR for correctness, pattern compliance, testing, accessibility, and safety. Produces a confidence score for merge readiness.
npx skills add https://github.com/microsoft/fluentui --skill review-pr
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.Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.
Use when implementation is complete, all tests pass, and you need to decide how to integrate the work - guides completion of development work by presenting structured options for merge, PR, or cleanup
Use when implementing any feature or bugfix, before writing implementation code
Use when encountering any bug, test failure, or unexpected behavior, before proposing fixes
Use when about to claim work is complete, fixed, or passing, before committing or creating PRs - requires running verification commands and confirming output before making any success claims; evidence before assertions always
Expert guidance for systematic backtesting of trading strategies. Use when developing, testing, stress-testing, or validating quantitative trading strategies. Covers "beating ideas to death" methodology, parameter robustness testing, slippage modeling, bias prevention, and interpreting backtest results. Applicable when user asks about backtesting, strategy validation, robustness testing, avoiding overfitting, or systematic trading development.
Cloud laboratory platform for automated protein testing and validation. Use when designing proteins and needing experimental validation including binding assays, expression testing, thermostability measurements, enzyme activity assays, or protein sequence optimization. Also use for submitting experiments via API, tracking experiment status, downloading results, optimizing protein sequences for better expression using computational tools (NetSolP, SoluProt, SolubleMPNN, ESM), or managing protein design workflows with wet-lab validation.
This skill should be used for time series machine learning tasks including classification, regression, clustering, forecasting, anomaly detection, segmentation, and similarity search. Use when working with temporal data, sequential patterns, or time-indexed observations requiring specialized algorithms beyond standard ML approaches. Particularly suited for univariate and multivariate time series analysis with scikit-learn compatible APIs.
Take microsoft/review-pr from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.