Review a Pull Request
Review PR $ARGUMENTS and produce a confidence score for merge readiness.
Phase 1: Gather PR Context
# 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
Phase 2: Classify PR Type
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.
Phase 3: Run Checks
Run each check category. For each finding, assign a severity:
- BLOCKER — must fix before merge
- WARNING — should address
- INFO — consider
A. Beachball Change File
Required if any published package source code changed (not just tests/stories/docs).
- Check
change/ directory in the diff for new .json files
- Verify change type:
patch for fixes, minor for features, never major without explicit approval
- Not required for changes that only affect tests, stories, docs, or snapshots
BLOCKER if missing for published source changes.
B. V9 Component Pattern Compliance
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
C. Dependency Layer Violations
For changes to package.json files or new imports in Tier 3 component packages:
- BLOCKER if a Tier 3 package (
react-button, react-menu, etc.) adds a dependency on another Tier 3 package
- Allowed Tier 2 deps:
react-utilities, react-theme, react-shared-contexts, react-tabster, react-positioning, react-portal
- Allowed Tier 1 deps:
@griffel/react, @fluentui/tokens, @fluentui/react-jsx-runtime
Reference: docs/architecture/layers.md
D. SSR Safety
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').
E. Testing
| 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 |
F. API Surface
| 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 |
G. Accessibility
| 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 |
H. Security and Quality
| 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 |
I. Documentation coverage
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:
- Pass 1 — user-facing docs. Component consumers (storybook stories, migration guides, docsite, MDX, change-file comments).
- Pass 2 — harness / agent-facing docs. Future agent sessions reading the skills,
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.
Pass 1 — user-facing docs
| 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 |
Pass 2 — harness engineering / agent-facing docs
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. |
When docs are NOT expected (default to PASS)
- Pure bug fix that restores documented behavior
- Internal refactor with no exported-symbol or behavior change
- Build / CI changes that have no contributor-facing or skill-facing impact
- Test-only PRs
- Style-only PRs (typo, formatting) on internal code
Severity
| 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 |
How to actually run this check
The model should walk the diff, not guess.
- Group changed files into surfaces. Code surfaces (
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).
- If the PR is purely a doc/harness surface, the check is PASS — it's documentation already.
- For each row in the user-facing table that matches a code change in the diff, verify the corresponding doc surface was also touched. If not, raise the matching severity.
- For each row in the harness-engineering table that matches, verify the corresponding skill or harness doc was also touched. Be specific: name the skill that needs updating, not just "a skill."
- Cross-search: when a PR renames a path, target, or label, run
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.
- Read the PR body before reporting. Authors often pre-empt with "docs follow-up tracked in #NNNN" or "harness skills updated in commit X" — those move findings from WARNING to INFO when the deferral is explicit and reasonable.
Phase 4: Calculate Confidence Score
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:
- 90–100: High confidence — safe to merge
- 70–89: Moderate confidence — minor concerns
- 50–69: Low confidence — needs attention
- 0–49: Not safe to merge — blockers present
Phase 5: Produce Output
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>
Phase 6: Post the review back to the PR
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:
- Confirm the active
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>.
- Don't post on PRs from your own branches unless explicitly asked — self-review comments are noise.
- If
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:
- The user explicitly asks for a review without posting ("review but don't post").
- The PR has an existing comment from this skill within the last day on the same head SHA — avoid duplicate noise. Look for the
*Posted via the \/review-pr` skill.*` trailer.
- Draft PRs where the user is clearly still iterating (state=OPEN, isDraft=true, recent force-push) — offer to post but don't do it by default.
Notes
- For large PRs (50+ files), prioritize: published source files > test files > config. Note reduced confidence due to review scope.
- Draft PRs: still review but note WIP status.
- Merge conflicts: flag as BLOCKER if detected.
- The
### Confidence Score: NN/100 line must always appear on its own line for machine parsing.
1---2name: review-pr3description: Review a PR for correctness, pattern compliance, testing, accessibility, and safety. Produces a confidence score for merge readiness.4---56# Review a Pull Request78Review PR **$ARGUMENTS** and produce a confidence score for merge readiness.910## Phase 1: Gather PR Context1112```bash13# PR metadata14gh pr view $ARGUMENTS --json title,body,author,labels,files,additions,deletions,baseRefName,headRefName,state,isDraft,number1516# Changed files list17gh pr diff $ARGUMENTS --name-only1819# Full diff20gh pr diff $ARGUMENTS2122# CI status23gh pr checks $ARGUMENTS24```2526## Phase 2: Classify PR Type2728Determine the PR type from changed files and metadata:2930| Type | Detection | Check scope |31| ---------------- | -------------------------------------------------------------------- | ---------------------------------------------- |32| **docs-only** | All files are `*.md`, `docs/**`, `**/stories/**`, `**/.storybook/**` | Change file only |33| **test-only** | All files are `*.test.*`, `*.spec.*`, `**/testing/**` | Change file + test quality |34| **bug-fix** | Branch starts with `fix/` or title contains "fix" | All checks, extra weight on tests |35| **feature** | Branch starts with `feat/` or adds new exports | All checks, extra weight on API + patterns |36| **refactor** | No new exports, restructures existing code | All checks, extra weight on no behavior change |37| **config/infra** | Changes to CI, configs, scripts only | Change file + no regressions |3839For **v8 packages** (`packages/react/`): skip V9 pattern checks — those are maintenance-only with different patterns.40For **web-components** (`packages/web-components/`): skip React-specific checks.4142## Phase 3: Run Checks4344Run each check category. For each finding, assign a severity:4546- **BLOCKER** — must fix before merge47- **WARNING** — should address48- **INFO** — consider4950### A. Beachball Change File5152Required if any published package source code changed (not just tests/stories/docs).5354- Check `change/` directory in the diff for new `.json` files55- Verify change type: `patch` for fixes, `minor` for features, never `major` without explicit approval56- Not required for changes that only affect tests, stories, docs, or snapshots5758**BLOCKER** if missing for published source changes.5960### B. V9 Component Pattern Compliance6162Only for files in `packages/react-components/react-*/library/src/`:6364| Check | Look for | Severity |65| ------------------- | --------------------------------------------------------------------------------------------------- | -------- |66| No `React.FC` | `React.FC`, `: FC<`, `React.FunctionComponent` in added lines | BLOCKER |67| No hardcoded styles | Hex colors `#[0-9a-fA-F]{3,8}`, hardcoded `px` values for spacing/radius/font in `.styles.ts` files | WARNING |68| Griffel usage | Style files must use `makeStyles` from `@griffel/react`, not inline styles | WARNING |69| mergeClasses order | User `className` must be the LAST argument in `mergeClasses()` | WARNING |70| Slot system | New components must use `slot.always`/`slot.optional` and `assertSlots` | WARNING |7172Reference: [docs/architecture/component-patterns.md](../../../docs/architecture/component-patterns.md)7374### C. Dependency Layer Violations7576For changes to `package.json` files or new imports in Tier 3 component packages:7778- **BLOCKER** if a Tier 3 package (`react-button`, `react-menu`, etc.) adds a dependency on another Tier 3 package79- Allowed Tier 2 deps: `react-utilities`, `react-theme`, `react-shared-contexts`, `react-tabster`, `react-positioning`, `react-portal`80- Allowed Tier 1 deps: `@griffel/react`, `@fluentui/tokens`, `@fluentui/react-jsx-runtime`8182Reference: [docs/architecture/layers.md](../../../docs/architecture/layers.md)8384### D. SSR Safety8586Grep added lines for unguarded browser API access:8788| Pattern | Severity |89| ------------------------------------------------------------- | -------- |90| `window.` without `canUseDOM` or `typeof window` guard nearby | BLOCKER |91| `document.` without guard | BLOCKER |92| `navigator.` without guard | BLOCKER |93| `localStorage` / `sessionStorage` without guard | BLOCKER |94| `instanceof HTMLElement` | WARNING |9596Check 3 lines above each match for a guard (`canUseDOM`, `typeof window !== 'undefined'`).9798### E. Testing99100| Check | Severity |101| --------------------------------------------------------------------------- | -------- |102| Source files changed but no corresponding `.test.tsx` changes | WARNING |103| New component missing `testing/isConformant.ts` | WARNING |104| Snapshot files need updating (render/style changes without `.snap` updates) | INFO |105106### F. API Surface107108| Check | Severity |109| ------------------------------------------------- | -------- |110| Public API changed but `etc/*.api.md` not updated | WARNING |111| Existing exports removed (breaking change) | BLOCKER |112| New exports added (flag for human review) | INFO |113114### G. Accessibility115116| Check | Severity |117| -------------------------------------------------- | -------- |118| Existing `aria-*` attributes removed | BLOCKER |119| `onClick` without `onKeyDown`/`onKeyUp` handler | WARNING |120| Interactive elements missing `role` or `aria-*` | WARNING |121| Images/icons without `aria-label` or `aria-hidden` | WARNING |122123### H. Security and Quality124125| Check | Severity |126| --------------------------------------------- | -------- |127| `eval()` or `new Function()` | BLOCKER |128| `dangerouslySetInnerHTML` | WARNING |129| `console.log` / `debugger` in production code | WARNING |130| `// @ts-ignore` without explanation | WARNING |131| `any` type in new code | INFO |132133### I. Documentation coverage134135A 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:136137- **Pass 1 — user-facing docs.** Component consumers (storybook stories, migration guides, docsite, MDX, change-file comments).138- **Pass 2 — harness / agent-facing docs.** Future agent sessions reading the skills, `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.139140Both 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.141142#### Pass 1 — user-facing docs143144| Source change | Expected doc surface |145| --------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |146| 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 |147| 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) |148| 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`) |149| 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 |150| Removal or deprecation of a public export | `library/docs/MIGRATION.md` entry and/or a `@deprecated` JSDoc on the symbol |151| 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 |152153#### Pass 2 — harness engineering / agent-facing docs154155This 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.156157Walk the diff for any of these and check whether the matching agent-facing doc was updated:158159| Source change | Expected harness update |160| --------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |161| 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. |162| New / renamed top-level `package.json` script | Same. Plus `docs/workflows/contributing.md` if it's contributor-facing. |163| 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. |164| 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`. |165| 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. |166| 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. |167| 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). |168| New skill, agent hook, or `.claude/`-level configuration | `AGENTS.md` skills table + the `.claude/skills/<name>/SKILL.md` bridge file. |169| 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. |170| 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. |171| 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. |172173#### When docs are NOT expected (default to PASS)174175- Pure bug fix that restores documented behavior176- Internal refactor with no exported-symbol or behavior change177- Build / CI changes that have no contributor-facing or skill-facing impact178- Test-only PRs179- Style-only PRs (typo, formatting) on internal code180181#### Severity182183| Situation | Severity |184| ----------------------------------------------------------------------------------------------------------------------- | -------- |185| Public export removed or behavior-changed without `MIGRATION.md` entry | BLOCKER |186| Skill references a renamed or removed path / target / label that this PR changes (skill will literally fail next run) | BLOCKER |187| New public component / new public hook with no story or MDX | WARNING |188| New public prop with no story exercising it | WARNING |189| New design token / className constant with no usage example | WARNING |190| Behavior change to public defaults with no PR-body callout | WARNING |191| New label / project field / nx target / convention without the matching skill or `AGENTS.md` / `docs/workflows/` update | WARNING |192| New first-time-setup requirement without `contributing.md` mention | WARNING |193| PR description explicitly defers docs to a follow-up (link cited) | INFO |194| Docs / harness change exists but feels minimal — could be more thorough | INFO |195196#### How to actually run this check197198The model should walk the diff, not guess.1992001. **Group changed files into surfaces.** Code surfaces (`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`).2012. **If the PR is purely a doc/harness surface**, the check is PASS — it's documentation already.2023. **For each row in the user-facing table that matches a code change in the diff**, verify the corresponding doc surface was also touched. If not, raise the matching severity.2034. **For each row in the harness-engineering table that matches**, verify the corresponding skill or harness doc was also touched. Be specific: name the skill that needs updating, not just "a skill."2045. **Cross-search**: when a PR renames a path, target, or label, run `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.2056. **Read the PR body** before reporting. Authors often pre-empt with "docs follow-up tracked in #NNNN" or "harness skills updated in commit X" — those move findings from WARNING to INFO when the deferral is explicit and reasonable.206207## Phase 4: Calculate Confidence Score208209```210Start at 100211212For each BLOCKER: -25 points213For each WARNING: -5 points214For each INFO: -1 point215216Bonuses:217 +5 if tests added/updated alongside source changes218 +3 if change file present and well-described219 +2 if PR description is thorough220221Floor at 0, cap at 100.222```223224Score interpretation:225226- **90–100**: High confidence — safe to merge227- **70–89**: Moderate confidence — minor concerns228- **50–69**: Low confidence — needs attention229- **0–49**: Not safe to merge — blockers present230231## Phase 5: Produce Output232233Use this exact format (also used verbatim as the PR-comment body in Phase 6 — don't duplicate work).234235Start 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.236237```238### Confidence Score: <score>/100239240<one-sentence summary>241242### Findings243244#### Blockers (must fix before merge)245- [ ] <finding with file:line reference>246247#### Warnings (should address)248- [ ] <finding with file:line reference>249250#### Info (consider)251- <finding>252253### Category Breakdown254255| Category | Status | Notes |256|----------|--------|-------|257| Change file | PASS/FAIL | ... |258| V9 patterns | PASS/WARN | ... |259| Dep layers | PASS/FAIL | ... |260| SSR safety | PASS/WARN | ... |261| Testing | PASS/WARN | ... |262| API surface | PASS/WARN | ... |263| Accessibility | PASS/WARN | ... |264| Security/Quality | PASS/WARN | ... |265| Docs coverage | PASS/WARN/FAIL | ... |266267### Recommendation268269APPROVE / REQUEST_CHANGES / COMMENT270271<brief rationale>272```273274## Phase 6: Post the review back to the PR275276After 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).277278Save the output from Phase 5 to a temp file so the markdown isn't mangled by shell quoting, then:279280```bash281gh pr comment $ARGUMENTS --repo microsoft/fluentui --body-file /tmp/pr-review-$ARGUMENTS.md282```283284Append a single trailer line to the body so the post is identifiable:285286```287---288*Posted via the `/review-pr` skill.*289```290291The 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.292293**Pre-checks before posting:**2942951. Confirm the active `gh` account has write access to the PR's repo (EMU accounts read fine but silently fail on writes):296 ```bash297 gh api graphql -f query='{ viewer { login } repository(owner:"microsoft", name:"fluentui") { viewerPermission } }'298 ```299 If `viewerPermission` is `NONE`, stop and ask the user to `gh auth switch --user <non-emu-account>`.3002. Don't post on PRs from your own branches unless explicitly asked — self-review comments are noise.3013. If `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.302303**When to skip posting:**304305- The user explicitly asks for a review without posting ("review but don't post").306- The PR has an existing comment from this skill within the last day on the same head SHA — avoid duplicate noise. Look for the `*Posted via the \`/review-pr\` skill.\*` trailer.307- Draft PRs where the user is clearly still iterating (state=OPEN, isDraft=true, recent force-push) — offer to post but don't do it by default.308309## Notes310311- For large PRs (50+ files), prioritize: published source files > test files > config. Note reduced confidence due to review scope.312- Draft PRs: still review but note WIP status.313- Merge conflicts: flag as BLOCKER if detected.314- The `### Confidence Score: NN/100` line must always appear on its own line for machine parsing.