PR Review — Pre-Raise Self Review
Attribution: Coverage and severity model from the PR & Code Review Playbook
by Mohamed ElZanaty (Engineering Manager, VOXI — vfuk-digital/Digital). Review
standard from Google's Code Review Developer Guide (google.github.io/eng-practices).
Run this on your own branch before you raise the PR. It reads your local diff,
reviews every changed line the way a senior reviewer would, and reports only findings
worth acting on — so the PR you raise is already clean. No GitHub, no CI, no posting:
it runs locally and reports back to you.
When this skill activates
- "Review my changes" / "review my diff" / "self-review this branch"
- "Is this ready to raise a PR?" / "what would a reviewer flag?"
- After finishing a feature/fix, before pushing or opening the PR
The standard
Judge the change against one question: does this improve the overall code health of
the system? Report what blocks that. Do not demand perfection — a clean, correct,
self-contained change is ready, even if not ideal.
Signal over noise. This is a self-review tool, not a linter. Report real issues a
senior engineer would stop on. Suppress nitpicks a human wouldn't bother raising, and
anything a linter/typechecker/CI already catches.
Phase 0 — Get the diff
- Read the change.
git diff <base>...HEAD (or staged: git diff --cached).
<base> = the branch you'll target (usually main/master/develop).
- Read the intent. Infer what this change is for from the diff and commit messages.
If intent is genuinely unclear, say so — don't invent it.
- Classify — picks what to weight hardest:
| Type |
Weight hardest on |
| Feature |
Design, logic correctness, tests, separation of concerns, UX/a11y |
| Bugfix |
Root cause vs symptom, regression test added, blast radius |
| Refactor |
Behavior preserved (no functional change smuggled in), test parity |
| API / schema |
Backward compatibility, versioning, consumer impact |
| Config / infra |
Secrets, rollback path, environment differences |
Size note: > ~400 LOC or mixed concerns (refactor + feature + fix in one) → say so
up front and recommend splitting before raising. Small, self-contained PRs review faster
and ship fewer bugs.
Phase 1 — Design pass (before line nits)
A line-perfect change on the wrong approach is still wrong. Quick check first:
- Does this belong here? Right layer, right module — or bolted where it doesn't fit.
- Sound approach? Fights existing patterns? Reinvents a helper that already exists?
- Right time? Or speculative work (YAGNI) for a need that doesn't exist yet.
A wrong-approach finding is a Blocker no matter how clean the code is — surface it
first, before line-level comments.
Phase 2 — Scan every changed line
Read every line of human-written code in the diff. (Generated/vendored/large data files:
scan, don't read line-by-line — and say so.) Look for:
Bugs & correctness
- Logic does what was intended; edge/failure paths handled (null/empty/boundary/error).
- No unexpected side effects or shared/global state mutation.
- Async/concurrent: every promise chain / listener traced, no races on shared state.
Security & safety
- No secrets, tokens, credentials, or debug code committed.
- No PII or sensitive data in logs.
- Inputs validated at true boundaries (user input, external APIs).
Tests
- Critical paths and edge cases covered. Bugfix → has a regression test.
- A major logic change with no test update is a Blocker.
Complexity (over-engineering is a finding)
- A future reader can understand it quickly. No speculative generality.
Contracts & compatibility
- API/schema changes are backward-compatible or versioned; no silent consumer break.
Structure & consistency
- Right layer, clear naming, follows codebase conventions; reuses existing helpers.
- Comments explain why, not what.
Performance
- Acceptable under expected load — loops, N+1 queries, DOM thrash, memory growth.
User-facing (if UI)
- Matches design; a11y (ARIA, keyboard, focus); responsive at key breakpoints.
Red flags — always a Blocker
| Red flag |
Why |
| Hardcoded secret / credential |
Leak — rotate and remove from history |
| Logging PII or sensitive data |
Privacy/compliance breach |
Empty catch with no context |
Silently swallows failures |
| Major logic change, no test update |
Unverifiable; regression risk |
| Wrong design / wrong layer |
Polished code on a bad foundation |
| Leftover debug code, logs, commented-out blocks |
Ships noise |
Filter before reporting (keep it low-noise)
Drop these — they are not worth a finding:
- Pre-existing issues on lines this change didn't touch.
- Anything a linter, typechecker, formatter, or CI already catches (imports, types,
style, newlines, test failures) — assume CI runs separately.
- Pedantic nitpicks a senior engineer wouldn't raise.
- Intentional changes clearly related to the broader goal.
- Suggestions you can't tie to a concrete failing case — if you're not confident it's
real, don't report it.
Each reported finding gets one severity:
| Severity |
Tag |
Blocks raising? |
| Blocker |
issue: |
Yes — fix before raising |
| Issue |
issue: |
Yes — fix before raising |
| Suggestion |
suggestion: |
No — author's call |
| Question |
question: |
No — clarify intent |
Phrase findings as Conventional Comments:
tag: file:line — <problem>. <fix>.
Output
Verdict: Ready to raise | Fix blockers first | Split recommended
Code health: Improves | Neutral | Degrades
Change type: [feature | bugfix | refactor | api | config]
Size: [LOC, atomic? or "split recommended"]
Blockers: [count, or "none"]
Findings (most severe first):
issue: file:line — <problem>. <fix>.
suggestion: file:line — <better approach>.
question: file:line — <what's unclear>.
Verdict rules:
- Ready to raise — no open Blockers/Issues and the change improves code health.
Suggestions can remain; they don't block.
- Fix blockers first — any Blocker/Issue, or design that degrades code health.
- Found nothing? Say "Ready to raise — no issues." Don't invent findings to look thorough.
Hard rules
- Run on the local diff — never invent code that isn't in it.
- Report real issues only — signal over noise; suppress what CI/linters catch.
- Review design before lines — a wrong approach blocks no matter how clean the code.
- Severity gates the verdict — Blockers/Issues block raising; suggestions never do.
- Secrets, PII-in-logs, empty catch blocks are always Blockers — no exceptions.
- A major logic change with no test update is always a Blocker.
- Don't demand perfection — a clean, correct, self-contained change is ready to raise.
- Never report a finding you can't tie to a concrete failing case.
1---2name: pr-review3description: Self-review your changes before raising a PR. Runs on your local branch/diff, scans every changed line for real bugs, security holes, missing tests, and design problems, filters false positives, and reports severity-tagged findings plus a ready-to-raise verdict. Triggers on "review my changes", "review my diff before I push", "is this ready to raise a PR", "self-review this branch".4---56# PR Review — Pre-Raise Self Review78> **Attribution:** Coverage and severity model from the *PR & Code Review Playbook*9> by Mohamed ElZanaty (Engineering Manager, VOXI — vfuk-digital/Digital). Review10> standard from Google's *Code Review Developer Guide* (google.github.io/eng-practices).1112Run this on your own branch **before you raise the PR**. It reads your local diff,13reviews every changed line the way a senior reviewer would, and reports only findings14worth acting on — so the PR you raise is already clean. No GitHub, no CI, no posting:15it runs locally and reports back to you.1617## When this skill activates1819- "Review my changes" / "review my diff" / "self-review this branch"20- "Is this ready to raise a PR?" / "what would a reviewer flag?"21- After finishing a feature/fix, before pushing or opening the PR2223## The standard2425Judge the change against one question: **does this improve the overall code health of26the system?** Report what blocks that. Do not demand perfection — a clean, correct,27self-contained change is ready, even if not ideal.2829**Signal over noise.** This is a self-review tool, not a linter. Report real issues a30senior engineer would stop on. Suppress nitpicks a human wouldn't bother raising, and31anything a linter/typechecker/CI already catches.3233---3435## Phase 0 — Get the diff36371. **Read the change.** `git diff <base>...HEAD` (or staged: `git diff --cached`).38 `<base>` = the branch you'll target (usually `main`/`master`/`develop`).392. **Read the intent.** Infer what this change is for from the diff and commit messages.40 If intent is genuinely unclear, say so — don't invent it.413. **Classify** — picks what to weight hardest:4243| Type | Weight hardest on |44|---|---|45| Feature | Design, logic correctness, tests, separation of concerns, UX/a11y |46| Bugfix | Root cause vs symptom, regression test added, blast radius |47| Refactor | Behavior preserved (no functional change smuggled in), test parity |48| API / schema | Backward compatibility, versioning, consumer impact |49| Config / infra | Secrets, rollback path, environment differences |5051**Size note:** > ~400 LOC or mixed concerns (refactor + feature + fix in one) → say so52up front and recommend splitting before raising. Small, self-contained PRs review faster53and ship fewer bugs.5455---5657## Phase 1 — Design pass (before line nits)5859A line-perfect change on the wrong approach is still wrong. Quick check first:6061- **Does this belong here?** Right layer, right module — or bolted where it doesn't fit.62- **Sound approach?** Fights existing patterns? Reinvents a helper that already exists?63- **Right time?** Or speculative work (YAGNI) for a need that doesn't exist yet.6465A wrong-approach finding is a **Blocker** no matter how clean the code is — surface it66first, before line-level comments.6768---6970## Phase 2 — Scan every changed line7172Read every line of human-written code in the diff. (Generated/vendored/large data files:73scan, don't read line-by-line — and say so.) Look for:7475**Bugs & correctness**76- Logic does what was intended; edge/failure paths handled (null/empty/boundary/error).77- No unexpected side effects or shared/global state mutation.78- Async/concurrent: every promise chain / listener traced, no races on shared state.7980**Security & safety**81- No secrets, tokens, credentials, or debug code committed.82- No PII or sensitive data in logs.83- Inputs validated at true boundaries (user input, external APIs).8485**Tests**86- Critical paths and edge cases covered. Bugfix → has a regression test.87- A major logic change with no test update is a **Blocker**.8889**Complexity** *(over-engineering is a finding)*90- A future reader can understand it quickly. No speculative generality.9192**Contracts & compatibility**93- API/schema changes are backward-compatible or versioned; no silent consumer break.9495**Structure & consistency**96- Right layer, clear naming, follows codebase conventions; reuses existing helpers.97- Comments explain **why**, not what.9899**Performance**100- Acceptable under expected load — loops, N+1 queries, DOM thrash, memory growth.101102**User-facing (if UI)**103- Matches design; a11y (ARIA, keyboard, focus); responsive at key breakpoints.104105---106107## Red flags — always a Blocker108109| Red flag | Why |110|---|---|111| Hardcoded secret / credential | Leak — rotate and remove from history |112| Logging PII or sensitive data | Privacy/compliance breach |113| Empty `catch` with no context | Silently swallows failures |114| Major logic change, no test update | Unverifiable; regression risk |115| Wrong design / wrong layer | Polished code on a bad foundation |116| Leftover debug code, logs, commented-out blocks | Ships noise |117118---119120## Filter before reporting (keep it low-noise)121122Drop these — they are not worth a finding:123124- Pre-existing issues on lines this change didn't touch.125- Anything a linter, typechecker, formatter, or CI already catches (imports, types,126 style, newlines, test failures) — assume CI runs separately.127- Pedantic nitpicks a senior engineer wouldn't raise.128- Intentional changes clearly related to the broader goal.129- Suggestions you can't tie to a concrete failing case — if you're not confident it's130 real, don't report it.131132Each reported finding gets one severity:133134| Severity | Tag | Blocks raising? |135|---|---|---|136| **Blocker** | `issue:` | Yes — fix before raising |137| **Issue** | `issue:` | Yes — fix before raising |138| **Suggestion** | `suggestion:` | No — author's call |139| **Question** | `question:` | No — clarify intent |140141Phrase findings as [Conventional Comments](https://conventionalcomments.org):142`tag: file:line — <problem>. <fix>.`143144---145146## Output147148```149Verdict: Ready to raise | Fix blockers first | Split recommended150Code health: Improves | Neutral | Degrades151Change type: [feature | bugfix | refactor | api | config]152Size: [LOC, atomic? or "split recommended"]153Blockers: [count, or "none"]154155Findings (most severe first):156 issue: file:line — <problem>. <fix>.157 suggestion: file:line — <better approach>.158 question: file:line — <what's unclear>.159```160161Verdict rules:162- **Ready to raise** — no open Blockers/Issues and the change improves code health.163 Suggestions can remain; they don't block.164- **Fix blockers first** — any Blocker/Issue, or design that degrades code health.165- Found nothing? Say "Ready to raise — no issues." Don't invent findings to look thorough.166167---168169## Hard rules1701711. Run on the local diff — never invent code that isn't in it.1722. Report real issues only — signal over noise; suppress what CI/linters catch.1733. Review design before lines — a wrong approach blocks no matter how clean the code.1744. Severity gates the verdict — Blockers/Issues block raising; suggestions never do.1755. Secrets, PII-in-logs, empty catch blocks are always Blockers — no exceptions.1766. A major logic change with no test update is always a Blocker.1777. Don't demand perfection — a clean, correct, self-contained change is ready to raise.1788. Never report a finding you can't tie to a concrete failing case.