The PR review that explains what actually changed.
A GitHub Action. One comment. Zero services to authorize.
~30s per PR Β Β·Β $0 service cost Β Β·Β 1 YAML file to install Β Β·Β runs on your own runner
- Why Andy
- Install β one YAML file
- Anatomy of the comment
- A peek at the output
- On a real PR
- How it works
- Re-run on demand with
/driftcomments - Configuration
- What Andy doesn't do
- The landing page is part of the action
- Local development & reproducible screenshots
- Repository layout
- CI / CD
- License & contact
Three questions every PR comment fails to answer:
| 01 Reviewers approve PRs they didn't fully understand because nobody has 90 minutes to trace a 100-file diff. Bugs ship in the gap between looks right and is right. | 02 Context lives in Linear, Slack threads, and the author's head β not in the PR. New reviewers spend half their time inferring intent before they can judge the code. | 03 No one can answer in dollars or minutes. The PR closes, the impact disappears into vibes, and the team forgets what they shipped by next quarter. |
Andy reads every pull request and posts one comment with the answers.
Drop this into .github/workflows/drift.yml. No tokens, no profile commands, no extra config.
name: Drift
on:
pull_request:
types: [opened, synchronize, reopened]
permissions:
contents: read
pull-requests: write
checks: write
models: read
jobs:
drift:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- uses: refactorlab/drift@mainThen open a PR β Andy auto-detects the latest profiler release, caches it via $RUNNER_TOOL_CACHE, and posts a sticky comment within ~30 seconds.
flowchart LR
PR((PR Β· N files)) --> Andy((π§ Andy))
Andy --> A1[π Architecture map]
Andy --> A2[π Value card]
Andy --> A3[π‘ Ranked suggestions]
Andy --> A4[β Risk quadrant]
Andy --> A5[π Hot-touch mindmap]
Andy --> A6[π§ Business context]
classDef src fill:#1f6feb,stroke:#1d4ed8,color:#fff
classDef hub fill:#ff6b3d,stroke:#ff9558,color:#fff
classDef out fill:#fafaf7,stroke:#d4d2cb,color:#1a1a1a
class PR src
class Andy hub
class A1,A2,A3,A4,A5,A6 out
One sticky comment per pull request, re-rendered on every push. Inside it: the visuals that turn a large diff into a guided handoff, plus the code suggestions and risks that actually need a reviewer's eye.
| # | Artifact | What it is |
|---|---|---|
| 1 | π Architecture map | Before β after diagrams of what your PR changed, plus the data structures connecting the two. Rendered as Mermaid. |
| 2 | π Value card | Money, customer, runtime, runtime-UX β each scored with the formula, the inputs that produced it, and a confidence label. |
| 3 | π‘ Ranked suggestions | Every code suggestion ships with a confidence score, a category, an applyable diff, and references to specs or docs. |
| 4 | Severity Γ likelihood map of every risk Andy spotted. Block on what's red before merge; monitor the rest. | |
| 5 | π Hot-touch mindmap | The files reviewers should open first, grouped by subsystem β the difference between a 100-file PR and a 6-file mental model. |
| 6 | π§ Business context | A product-level diagram with the slice your PR touches highlighted β so reviewers see why the change exists, not just what. |
Two artifacts from a real review on a 100-file PR β the value card, a ranked product-correctness suggestion with a fixable diff, and the self-drawing architecture map underneath:
|
|
π Open the full example review β
This is exactly what reviewers see when Andy lands on a real pull request (refactorlab/drift#36) β the sticky comment with a live architecture flow, weighted scores, and grouped findings:
- Runs as a GitHub Action on your own runner. Nothing leaves the workflow β no service to authorize, no API key to manage.
- Auto-detects the latest profiler release (
drift-static-profiler) and caches it via$RUNNER_TOOL_CACHEso subsequent runs are fast. - Walks the PR diff against the base branch, builds the call graph + data-structure map, and renders the comment.
- Posts (or updates) a single sticky comment β identified by a hidden marker, so it's overwritten in place on every push.
- Adds a
Drift / PR reviewcheck run summarising the verdict (advisory; does not fail the check).
Andy auto-runs on every push. Sometimes you want to re-run with different flags β bump the AI model, enable debug logs, or open a tracking issue. PR comments make that one click:
/drift
/drift debug=true
/drift ai-suggestions=false audio-summary=false
/drift ai-model=openai/gpt-5
/drift issue β also open / refresh a tracking issue for this PR
A fenced-YAML form is also accepted, useful for richer reruns:
/drift
```yaml
debug: true
ai-model: openai/gpt-5
fail-threshold: 0
```
UX: π acknowledges the command once the runner picks it up (~10β30 s), then π on success, π on failure, or π on a closed/merged PR. The sticky PR comment is updated in place β no duplicates.
start-on-pr-comment: true is additive β turning it on enables /drift comments without disabling the auto-run on pushes. One workflow file does both:
name: Drift
on:
pull_request:
types: [opened, synchronize, reopened]
issue_comment:
types: [created]
permissions:
contents: read
pull-requests: write
checks: write
models: read
issues: write # /drift issue + π / π / π / π reactions
jobs:
drift:
# Auto-run every pull_request event; for issue_comment, gate on
# author_association + /drift prefix + not-a-Bot.
if: >-
github.event_name != 'issue_comment' ||
(github.event.issue.pull_request != null &&
contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'),
github.event.comment.author_association) &&
startsWith(github.event.comment.body, '/drift') &&
github.event.comment.user.type != 'Bot')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- uses: refactorlab/drift@main
with:
start-on-pr-comment: trueThat's everything. No separate parser shell, no REST resolve step, no fork-safe checkout dance β the action handles it.
π Prefer two workflow files?
If you'd rather keep the comment trigger isolated (e.g. to grant issues:write only to that flow, or to enforce the security gate at the workflow boundary where an if: typo can't bypass it), copy examples/drift-on-comment.yml alongside the auto-run drift.yml. Both forms invoke the same action.
π§ What you can override per /drift
| Key | Default | Example |
|---|---|---|
debug |
false |
/drift debug=true |
progress |
true |
/drift progress=false |
ai-suggestions |
true |
/drift ai-suggestions=false |
audio-summary |
true |
/drift audio-summary=false |
ai-model |
openai/gpt-4.1 |
/drift ai-model=openai/gpt-5 |
ai-max-suggestions |
1 |
/drift ai-max-suggestions=5 |
fail-threshold |
(empty) | /drift fail-threshold=0 |
profiler-release-tag |
(latest) | /drift profiler-release-tag=drift-static-profiler-v0.6.0 |
tts-voice |
af_heart |
/drift tts-voice=am_michael |
open-issue |
false |
/drift issue (also /drift open-issue=true) |
Unknown keys log a ::warning:: and are dropped β forward-compatible against future inputs.
π‘οΈ Security model
The if: above is three gates in concert, all evaluated by GitHub before the job spins up:
github.event.issue.pull_request != nullβ only PR comments, never plain issue comments.author_association β {OWNER, MEMBER, COLLABORATOR}β drive-by commenters can't trigger.comment.user.type != 'Bot'β defends against Andy ever triggering itself.
The action re-checks #1 and #3 inside its comment-gate step as defense-in-depth. For fork PRs, the head is checked out by immutable SHA via refs/pull/<n>/head β a force-push between the π ack and the checkout cannot swap in different code. Drift only reads source (tree-sitter); it never executes fork code, so the usual fork-PR-with-secrets attack does not apply.
Andy works with zero configuration β just paste the YAML above.
π Permissions explained
| Permission | Why |
|---|---|
contents: read |
Read the diff and the base/head trees. |
pull-requests: write |
Create / update the sticky comment. |
checks: write |
Emit the Drift / PR review check run. |
models: read |
Read GitHub Models for LLM-assisted suggestion ranking. |
βοΈ Optional knobs (env or with-block)
| Variable | Default | Notes |
|---|---|---|
DRIFT_BASE_SHA |
inferred | Override the base SHA Andy diffs against. |
DRIFT_SUGGESTION_CONFIDENCE |
0.75 |
Floor for showing a code suggestion. |
DRIFT_DEV_HOUR_RATE_USD |
95 |
Used in the Money axis formula. |
π Click for the honesty section
- β Doesn't block merges. Findings surface as a check-run summary; you decide whether to gate on them.
- β Doesn't run your tests, or your code. Pure static analysis on the diff + call graph.
- β Doesn't talk to any external service beyond GitHub APIs. The whole pipeline runs on your runner.
- β Doesn't model new-feature dev time. The Money axis is the cost of servicing what the PR ships (bugs + maintenance + LLM iteration) β not the cost of building it.
- β Doesn't replace human review. It hands the next reviewer a guided map and a short list of things that warrant a second look.
- Marketplace: https://github.com/marketplace/actions/andy-pr-handoff-by-drift
- Issues: https://github.com/refactorlab/drift/issues
- Contact: schuldi@gmail.com
- License: MIT
Β© Refactor Labs Β· built to make code review feel less like archaeology.



