The pipeline
From webhook to review
What happens between the moment a developer pushes to a PR and the moment they see PrSentry's comments. Every step, with the actual code.
01
Webhook received
GitHub fires a pull_request event the moment a PR is opened, synchronized, or reopened. We respond 200 immediately and process the work in a background queue.
python · webhooks.py
@app.post(class="tok-str">"/webhooks/github")
async def gh_webhook(req: Request, bg: BackgroundTasks):
payload = await req.body()
verify_signature(payload, req.headers[class="tok-str">"x-hub-signature-256"])
event = req.headers[class="tok-str">"x-github-event"]
if event == class="tok-str">"pull_request":
bg.add_task(enqueue_review, json.loads(payload))
return {class="tok-str">"ok": True}02
Signature validated
Every webhook is signed with your GitHub App secret. We verify the signature in constant time before doing anything else. Mismatched signatures are dropped.
python · security.py
def verify_signature(body: bytes, header: str) -> None:
expected = class="tok-str">"sha256=" + hmac.new(
WEBHOOK_SECRET.encode(),
body,
hashlib.sha256,
).hexdigest()
if not hmac.compare_digest(expected, header):
raise HTTPException(401, class="tok-str">"bad signature")03
Diff fetched
We pull the unified diff using the installation token, parse it into per-file hunks, and capture the head SHA so comments anchor to the right commit.
04
Files filtered
Lockfiles, vendor dirs, generated code, and binaries get dropped before any token is spent. Patterns are configurable per-repo.
yaml · .prsentry.yml
skip:
- class="tok-str">"**/package-lock.json"
- class="tok-str">"**/yarn.lock"
- class="tok-str">"**/poetry.lock"
- class="tok-str">"**/uv.lock"
- class="tok-str">"**/*.min.js"
- class="tok-str">"**/dist/**"
- class="tok-str">"**/build/**"
- class="tok-str">"**/node_modules/**"
- class="tok-str">"**/__generated__/**"
- class="tok-str">"**/*.{png,jpg,gif,pdf,zip}"05
Token budget applied
Files are sorted by relevance and truncated to fit within the per-file ceiling. The PR-wide budget is a hard stop — we never exceed it.
06
Agent runs
A LangGraph agent orchestrates the review. The graph is intentionally simple: reviewer reads, calls tools, collects findings, and exits. ruff_check returns real linter output Claude can cite.
START
→
reviewer
↑↓
tools
→
collect
→
END
python · prompts.py — system prompt
You are PrSentry, a senior code reviewer.
You review pull request diffs with surgical precision.
Output inline review comments anchored to specific
lines in the diff. Each comment has:
- severity: CRITICAL | WARNING | SUGGESTION | NITPICK
- line: 1-indexed position in the new file
- body: concise, actionable, with a code suggestion
when possible
Rules:
1. Cite ruff findings when they exist; do not duplicate.
2. CRITICAL is reserved for security issues, data loss,
or runtime crashes — not style.
3. Skip lockfiles, generated code, and binary blobs.
4. If the diff is clean, say so in a single summary
comment. Do not invent issues.
You have tools: ruff_check, read_full_file, get_pr_metadata.
Use them liberally. Stop when you have enough signal.07
Review posted
Findings are batched into a single GitHub review (one API call) so the PR timeline stays clean. Inline comments anchor to the right side of the diff.
08
Run saved to DB
Status, duration, file count, comment count, token usage, and any errors are persisted. The Langfuse trace ID is stored alongside so you can jump straight to the agent transcript.