pr-review

Review PR changes against project standards and post feedback

SKILL.md

GitHub

/pr-review - Pull Request Review

Review PR changes against project standards for this user-agent parsing library.

Usage

/pr-review              # Review current branch's PR
/pr-review 123          # Review PR #123
/pr-review --comment    # Post review as PR comment

Workflow

Step 1: Get PR Context (run in parallel)

# Detect repo
REPO=$(git remote get-url origin | sed 's/.*github.com[:/]\(.*\)\.git/\1/' | sed 's/.*github.com[:/]\(.*\)/\1/')

gh pr view --json number,title,baseRefName,headRefName,files
git diff origin/main...HEAD --name-only
git diff origin/main...HEAD --stat

If a PR number was provided, use gh pr view <number> and gh pr diff <number> instead.

Step 2: Categorize Changed Files

Category Patterns
Parser Core user_agent_parser/parser.py
Advanced Engine user_agent_parser/advanced_engine.py
Analytics user_agent_parser/analytics.py
Public API user_agent_parser/__init__.py
Constants/Devices user_agent_parser/constants.py, user_agent_parser/modern_devices.py
Tests tests/**/*.py
CI/Config .github/workflows/*, pyproject.toml, poetry.lock
Docs *.md, CHANGELOG.md

Step 3: Apply Review Checks

Read changed files and verify against these standards:

Regex & Parser Correctness

Check Standard
Regex tested Every new/changed regex has real UA string test cases
browser_rules order New entries don't break priority (Edge before Chrome, Chrome before Safari, etc.)
_token bounds Every self._token[n] access has len() guard
Compiled patterns Hot-path regex uses cached compiled patterns, not re.compile() per call
ReDoS safety No nested quantifiers or overlapping alternations that cause catastrophic backtracking

Backward Compatibility

Check Standard
parse() 7-tuple parse() still returns (browser, browser_version, os, os_version, device_type, device_name, device_host)
Public API stable Functions in __init__.py / __all__ unchanged in signature
AdvancedResult fields No removed/renamed fields in AdvancedResult, BrowserCapabilities, SecurityFingerprint
DeviceCategory enum No removed/renamed enum values, only additions
Constants immutable OS, DeviceType, DeviceName attributes not changed, only extended

Python 3.8+ Compatibility

Check Standard
No match/case match/case requires 3.10+ — use if/elif
No union syntax X | Y in types requires 3.10+ — use Union[X, Y]
No TypeAlias Requires 3.10+ — use plain assignment
No ParamSpec Requires 3.10+ — use typing_extensions if needed

Cache Safety

Check Standard
lru_cache returns immutable Cached functions return tuples, not lists/dicts/sets
Cache key hashable All arguments to cached functions are hashable
No cache side effects Cached functions are pure (no mutations, no I/O)

Test Coverage

Check Standard
New features tested New parser/engine features have parametrized test cases
Real UA strings Tests use real-world user agent strings, not fabricated ones
Edge cases covered Empty string, None, very long strings, missing parentheses
New source files New .py files in user_agent_parser/ have test coverage

Thread Safety

Check Standard
Batch processing ThreadPoolExecutor usage includes proper cleanup (with statement)
No shared mutation Batch operations don't share mutable state across threads

Code Quality

Check Standard
Type hints Public functions have parameter and return type hints
CHANGELOG Non-trivial changes have CHANGELOG.md entry
Device code format MOBILE_DEVICE_CODE_NAME entries follow key→name format, no duplicates
Consistent naming New methods/constants follow existing naming patterns

Step 4: Generate Report

Structure findings by severity:

## PR Review: #<number> - <title>

**Files Changed:** N | **Additions:** +N | **Deletions:** -N

### 🔴 Critical (blocks merge)
- [ ] `user_agent_parser/parser.py:78` - `self._token[3]` accessed without bounds check
- [ ] `user_agent_parser/__init__.py:15` - `parse()` return changed from 7-tuple to 8-tuple — breaks all consumers
- [ ] `user_agent_parser/parser.py:45` - New browser regex `(Chrome|CriOS)(\d+)*` has nested quantifier — ReDoS risk

### 🟡 Standards (should fix)
- [ ] `user_agent_parser/advanced_engine.py:120` - Uses `match/case` — Python 3.10+ only, project supports 3.8+
- [ ] `tests/test_user_agent_parser.py` - New Brave detection has no parametrized test cases
- [ ] Missing CHANGELOG.md entry for new feature

### 🟢 Suggestions (nice to have)
- [ ] `user_agent_parser/parser.py:90` - Add type hint to `get_device_name()` return
- [ ] `user_agent_parser/advanced_engine.py:45` - Browser detection duplicates logic from parser.py — consider reuse

---
**Verdict:** APPROVE | REQUEST_CHANGES | COMMENT
*Review based on project standards in CLAUDE.md*

Step 5: Determine Verdict

Condition Verdict
No critical issues, 0-2 standards issues APPROVE
No critical issues, 3+ standards issues COMMENT
Any critical issues REQUEST_CHANGES

Step 6: Post Comment (if --comment)

Based on verdict:

# Clean — approve
gh pr review <number> --approve --body "$(cat <<'EOF'
<review-report>
EOF
)"

# Suggestions only — comment
gh pr review <number> --comment --body "$(cat <<'EOF'
<review-report>
EOF
)"

# Critical issues — request changes
gh pr review <number> --request-changes --body "$(cat <<'EOF'
<review-report>
EOF
)"

Quick Reference: What to Check by File

File Key Checks
parser.py Regex correctness, browser_rules order, _token bounds, lru_cache mutation, compiled pattern caching
advanced_engine.py AdvancedResult field stability, confidence bounds [0,1], DeviceCategory enum stability, ThreadPoolExecutor cleanup
analytics.py Thread safety in BatchProcessor, export format correctness
constants.py No changed existing values, consistent MOBILE_DEVICE_CODE_NAME format, no duplicate keys
modern_devices.py DeviceSpec format consistency
__init__.py Public API unchanged, all consistent, parse() 7-tuple preserved
tests/*.py Parametrized with real UA strings, edge cases covered, backward compat verified
CHANGELOG.md Entry exists for non-trivial changes
.github/workflows/* Python version matrix includes 3.8–3.15, codecov integration intact
pyproject.toml Python requirement still >=3.8, no incompatible dependency versions