Approval Drift: Why Code Changed After Approval Keeps Reaching Your Main Branch
An approved pull request isn't always approved code. Learn what engineering leaders on Reddit say about commits pushed after approval, self-merges and bypassed branch protection at enterprise scale, and how Keypup MCP uses approval dates, last commit dates, approval counts, required approvals, mergers, target branches and build status to measure how much code reaches your protected branches without anyone reviewing what actually shipped.
TL;DR: In large engineering organizations, a green "Approved" badge often covers code nobody reviewed. Commits are pushed after the last approval, pull requests merge with fewer approvals than required, and authors merge their own changes outside working hours. This is approval drift: the gap between the code that was approved and the code that shipped. Most review policies assume it doesn't happen; most audits find out it did. The Keypup MCP Server reads each pull request's last approval date, last commit date, last review date, approval count, required approval count, review state, author, merger, target branch, repository, lines changed, build status and merge date. Your AI assistant can then measure how much code reached your protected branches without a review of its final version, when and where it happens, who merges without a second pair of eyes, and which approved pull requests have changed since they were approved.
Every engineering organization has a rule that says code needs a review before it reaches production. Far fewer can say how often the code that reached production is the code that was reviewed. Between the reviewer's "LGTM" and the merge button, a lot can happen — and most dashboards only record that an approval exists.
The Friction: "Approved" Isn't the Same as "Reviewed"
In a team of six, everybody knows when a last-minute change slipped in after approval. In an organization with 1,500 engineers and 500 repositories, approval drift becomes invisible for structural reasons:
Approvals often survive new commits. Unless a repository is configured to dismiss stale approvals, an approval given on Tuesday still counts after the author pushes a 400-line change on Thursday.
Branch protection is configured repository by repository. Required approvals, admin bypasses and code owner rules drift apart as repositories are created by different teams over different years.
Exceptions become habits. "Just a typo fix after approval", "merging my own hotfix, nobody's online" — each one reasonable, together a sizeable share of what ships.
Audits sample; they don't measure. A SOX or SOC 2 auditor picks 25 changes and asks for evidence. Nobody knows the real rate across 60,000 merges until an auditor finds an exception.
The result: a review policy that looks enforced on paper, and a change history that tells a different story.
Why This Matters: The Real Cost of Approval Drift
The Risk Problem
The changes pushed after approval are rarely random. They are the last-minute fix, the "small" config tweak, the merge of a conflicting branch. These are exactly the changes that break production — and nobody but the author has read them.
The Compliance Problem
Separation of duties — the "four-eyes principle" — is a control in most SOX, SOC 2, ISO 27001 and PCI DSS programs. A self-merged pull request with no approval, or an approval that predates the final code, is an exception an auditor will ask you to explain. Explaining one is easy; explaining that you don't know how many there are is not.
The Trust Problem
When reviewers learn that what they approve isn't what merges, reviews become a formality. Authors learn that the approval is the gate, not the review. Both sides stop treating code review as the quality control it was designed to be.
The Enterprise Discussion: What Engineering Leaders Are Actually Saying
Stale approvals, admins bypassing branch protection and "approve now, I'll fix it after" come up again and again wherever engineering managers, staff engineers and security leads compare notes. The quotes below are representative of recurring threads on r/ExperiencedDevs, r/devops, r/EngineeringManagers and r/cybersecurity.
"Someone approved my PR, then I rebased and pushed a fix for the failing test, and it merged without anyone looking at it again. I only noticed because the fix was wrong. Our settings don't dismiss stale approvals and nobody had ever checked."
Engineering Manager, Enterprise SaaS Company (r/EngineeringManagers)
"Our SOC 2 auditor pulled 25 random changes and two were self-merged with no approval. Both were Friday evening hotfixes by senior engineers with admin rights. Now I need to tell them what the real rate is, and I have no idea."
Security Lead, Healthcare Group (r/cybersecurity)
"Every repo has branch protection, on paper. In practice half of them were set up years ago by different teams with different rules, and admins can bypass all of it. The policy says two reviewers. The data, if we had it, would say something else."
The pattern holds: leaders assume the review policy is enforced, but nobody measures whether the code that ships is the code that was approved.
How Keypup MCP Measures Approval Drift
The Keypup Model Context Protocol (MCP) Server gives your AI assistant structured access to the data Keypup imports from GitHub, GitLab, Bitbucket, Azure DevOps and Jira. For this analysis, what matters is each pull request's target branch and repository, its approval count and required approval count, its current review state, the date of its last approval, its last commit and its last submitted review, its author and the user who merged it, its lines changed, its build status and its merge date.
In the prompts below, the protected branches are the branches you name in the prompt — here main, master, production and release/* — not your platform's actual protection settings, which Keypup doesn't import. A pull request was changed after approval when its last commit is later than its last approval, and re-reviewed when a review was submitted after that last commit. Its final code was never reviewed when it merged with fewer approvals than required, or changed after approval without a later review. Durations are in business hours — Monday to Friday, 09:00 to 17:00 UTC by default; adjust the working hours and time zone to match your teams.
1. Running an Approval Integrity Health Check
The first question: how much of what reaches a protected branch was approved in its final form?
MCP Prompt:
For pull requests merged into main, master, production or
release branches in the last 12 months, show the number merged,
the share whose final code was never reviewed — fewer approvals
than required, or a commit after the last approval with no
review submitted after it — the share changed after approval,
and the share self-merged with zero approvals.
Output: Approval Integrity Health Check — Last 12 Months
Key Insight: Almost one merge in five reached a protected branch without anyone approving the code that actually shipped. Most of it is not missing reviews: 7,000 of the 11,790 pull requests had an approval — for an earlier version of the code.
2. Checking Whether Approval Drift Is Getting Better or Worse
A single rate hides when the rules get bent.
MCP Prompt:
As a line chart, show by month of merge over the last 12 months
the share of pull requests merged into protected branches that
were changed after their last approval without a later review,
and the share merged with fewer approvals than required.
Output: Is Approval Drift Getting Better or Worse?
Key Insight: Both lines jump by about three points in December, March, June and September. Quarter-end deadlines, not individual habits, are when review rules get bent — which is also when the riskiest changes ship.
3. Finding Which Repositories Ship Unreviewed Code
An organization-wide rate hides the repositories where drift concentrates.
MCP Prompt:
For pull requests merged into protected branches in the last 12
months, show for each of these repositories the share that was
approved with no later change, changed after approval then
re-reviewed, changed after approval and not re-reviewed, and
merged with fewer approvals than required, as 100% stacked bars.
Add the number merged.
Output: Which Repositories Ship Unreviewed Code?
Key Insight: Unreviewed final code ranges from 6% in payments-core to 38% in infra-terraform. In checkout-web and partner-api the problem is drift after approval; in data-platform and infra-terraform it's merges with too few approvals. Same company, same written policy — very different results, which usually points to how each repository's branch protection is configured.
4. Seeing When Unreviewed Code Gets Merged
Does approval drift happen at random, or at specific times?
MCP Prompt:
As a heatmap, show the share of pull requests merged into
protected branches in the last 12 months whose final code was
never reviewed, by day of week and hour band of the merge in UTC.
Output: When Does Unreviewed Code Get Merged?
Key Insight: Monday to Thursday working hours sit at 13–18%. Friday from 18:00 jumps to 42%, and weekends run at 44–55%. Merges outside working hours are the ones nobody else is around to approve — exactly where on-call fixes and "quick changes" end up. The hours are in UTC: read them against where your teams actually work.
5. Measuring How Far the Code Drifted After Approval
Not all drift is equal. A follow-up commit five minutes after "approve with suggestions" isn't the same as a rewrite two days later.
MCP Prompt:
For pull requests merged into protected branches in the last 12
months that had a commit after their last approval and no review
after that commit, group them by business hours between the last
approval and the last commit — under 1, 1 to 8, 8 to 40, 40 or
more. Show the number of pull requests as columns, the share
whose build status is not success as a line, and the median
lines changed.
Output: How Far Did the Code Drift After Approval?
Key Insight: Over half of the drift happens within an hour of approval, on the smallest pull requests — the profile of quick follow-up fixes. The 1,470 pull requests changed a full working day or more after approval are different: approved for one change, merged with another, and a quarter of them show a build status other than success. That's where a "dismiss stale approvals" rule pays for itself.
6. Seeing Who Merges Without a Second Pair of Eyes
The two repositories with the most under-approved merges deserve a closer look.
MCP Prompt:
For the data-platform and infra-terraform repositories, show
each person who merged pull requests into protected branches in
the last 12 months as a scatter plot: number of pull requests
merged against the share they authored and merged with zero
approvals. Size each point by the median lines changed. Hash
usernames.
Output: Who Merges Without a Second Pair of Eyes?
Key Insight: Two accounts explain most of the self-merging. Merger A merges many small changes alone — worth checking against your list of automation and release accounts before treating it as a policy breach. Merger B self-merges large pull requests, which is the conversation to have first. Hashing usernames keeps the first look about the pattern, not the person.
7. Listing Approved Pull Requests That Changed Since
The last step turns the analysis into a list someone can act on before the next merge.
MCP Prompt:
List open pull requests targeting protected branches whose review
state is approved but which have a commit after their last
approval and no review submitted after that commit. Show the
repository and number, target branch, lines changed, approvals
against required approvals, business hours since the last
approval, business hours between the last approval and the last
commit, and build status. Sort by time since approval.
Output: Approved Pull Requests That Changed Since
Pull Request
Target Branch
Lines Changed
Approvals / Required
Since Approval (bh)
Approval → Last Commit (bh)
Build Status
infra-terraform #2214
production
540
1 / 1
31.5
27.0
FAILURE
checkout-web #8841
main
1,120
2 / 2
22.0
19.5
SUCCESS
data-platform #5307
main
860
1 / 1
17.5
16.0
NONE
partner-api #3092
main
310
1 / 1
12.0
9.0
SUCCESS
mobile-app #6455
release/7.4
205
1 / 1
8.5
6.5
IN_PROGRESS
identity-service #2390
main
75
2 / 2
5.0
0.5
SUCCESS
checkout-web #8856
main
140
2 / 2
3.5
0.3
SUCCESS
Key Insight: Seven pull requests show as approved today, and none of them is exactly the code that was approved. The first four changed eight or more business hours after approval and one targets production with a failing build. These need a fresh review before anyone clicks merge; the last two changed within an hour of approval and need only a quick second look.
The Technical Implementation: What Keypup MCP Actually Reads
Every number above comes from fields Keypup already imports from your Git platforms. It helps to be precise about what they are — and what they aren't.
Drift Comes From Comparing Three Dates
For each pull request, Keypup records the date of the last approval, the date of the last commit and the date of the last submitted review. A last commit later than the last approval means the code changed after it was approved; a last review later than that commit means someone looked again. Any submitted review counts as a look, including a plain comment. Keypup compares dates — it doesn't diff the approved and merged versions — so a rebase, or merging the target branch into the pull request after approval, also counts as a change if it adds a commit with a later date. Measuring at the pull request level also means the Commits dataset, which carries each commit's size and message, can't be filtered by approval date on its own.
Approval Counts Depend on Your Platform's Rules
Each pull request carries its approval count and its required approval count, as exposed by your Git platform. If a repository has no required-review rule, its required count may be zero or a platform default: check how your repositories are configured before reading "fewer approvals than required" across all of them. Keypup doesn't import branch protection settings, admin bypasses or "dismiss stale approvals" options themselves — only their effect on each pull request.
Self-Merges Come From Author and Merger
Each merged pull request records its author and the user who merged it. A self-merge is a pull request where both are the same user. Bots and release accounts appear like any other user: filter them out by username. Keypup can hash usernames in a report, so you can look at the distribution before you look at names.
Protected Branches Are a Filter, Not a Setting
Keypup records each pull request's target branch and repository. "Protected branches" in this article means a list of branch names you put in the prompt; adjust it to your branching model.
Build Status Is the Current One
The build status is the latest status your Git platform exposes for the pull request — success, failure, in progress, action required or none. It isn't a history of every pipeline run, and pull requests in repositories without CI report none.
Business Hours and Merge Times Come From Formulas, Not a Calendar
Keypup's BUSINESS_HOURS function counts working time between two dates — Monday to Friday, 09:00 to 17:00 UTC by default — and accepts a different time zone, working hours and working days. Day of week and hour of merge are computed from the merge date. Neither knows about public holidays, on-call rotas or vacations: Keypup doesn't import calendars.
Real-World Impact: Enterprise Case Studies
Case Study 1: European Retail Bank
Before Keypup MCP:
1,300 engineers across 450 repositories, all in scope for SOX change management
Two audit findings in a row for self-approved changes
No organization-wide measure of how often the review control was bypassed
After Keypup MCP:
The health check showed approval drift, not missing reviews, as the larger share of the exposure, which moved the fix from "remind people to review" to repository settings
The repository breakdown identified 60 repositories where pull requests required zero approvals; platform engineering standardized branch protection across all of them in one quarter
The share of merges with unreviewed final code fell from 17% to 5% in two quarters, and the next audit sample came back clean
"For years we answered the auditor with our policy. Now we answer with the rate, by repository, every month. The conversation changed from 'prove it' to 'show us the trend'."
— CISO, European Retail Bank
Case Study 2: North American Logistics Platform
Before Keypup MCP:
800 engineers, 3,000+ pull requests merged a month
Production incidents traced back to "small" changes pushed after approval
On-call engineers with admin rights merging their own fixes at night and on weekends
After Keypup MCP:
The drift window analysis showed that changes made a working day or more after approval failed CI far more often; teams enabled "dismiss stale approvals" on all production repositories
The merge time heatmap made weekend self-merges visible; on-call fixes now get a mandatory next-business-day review, and the heatmap is rerun every month
Leads check the "approved but changed" list before each release, re-requesting review on anything that drifted
"We thought our problem was slow reviews. It was fast merges of code nobody had reviewed. The list of approved-but-changed pull requests is now part of every release checklist."
— VP Engineering, North American Logistics Platform
Implementation: Getting Started with Keypup MCP
1. Connect Your Git Platforms
Connect GitHub, GitLab, Bitbucket or Azure DevOps to Keypup. Pull requests, reviews, approvals, commits and merges are imported automatically.
2. Agree on What "Protected" Means
List the target branches that matter — main, production, release branches — and the bot and release accounts you want to exclude. Reuse the same list in every prompt so numbers stay comparable over time.
3. Check Required-Review Rules per Repository
Run the repository breakdown first. Repositories with a large share of "fewer approvals than required" merges often simply lack a required-review rule; fix the setting before you discuss behaviour.
4. Connect the MCP Server to Your AI Assistant
Follow the MCP documentation to connect Keypup to Claude, ChatGPT, Cursor or any MCP-compatible assistant.
5. Set a Baseline, Then Check Before Every Release
Run the health check and the monthly trend to set a baseline. Then bring the "approved but changed" list to every release, review the repository breakdown monthly, and share the trend with your compliance team each quarter.
Frequently Asked Questions
What is approval drift in code review?
Approval drift is the gap between the code a reviewer approved and the code that was merged. It happens when commits are pushed after the last approval and nobody reviews them before the pull request merges.
How do you detect commits pushed after approval?
Compare each pull request's last commit date with its last approval date. If the commit is later and no review was submitted after it, the final code wasn't reviewed. Keypup MCP does this from the dates it already imports for every pull request.
How do you measure self-merged pull requests?
Count merged pull requests where the merger is also the author, and check how many approvals they had. Keypup MCP reads the author, merger, approval count and required approval count of every pull request.
Does Keypup replace branch protection rules?
No. Branch protection prevents bypasses; Keypup MCP measures how often they still happen — by repository, branch, time and person — so you can see which rules are missing and show auditors the real rate.
The Bottom Line
An "Approved" badge only tells you that someone approved something. Keypup MCP shows how often what shipped is what was approved, and where it isn't:
✅ Measuring how much code reaches protected branches without a review of its final version ✅ Tracking approval drift month by month, including quarter-end spikes ✅ Comparing repositories to find missing required-review rules ✅ Finding when unreviewed code merges, by day and hour ✅ Separating harmless follow-ups from real drift by the time between approval and last commit ✅ Seeing who merges without a second pair of eyes, anonymized first ✅ Listing approved pull requests that changed since before anyone clicks merge
Ready to see how much of your shipped code was never reviewed in its final form? Connect the Keypup MCP Server and ask your AI assistant.
Ready to Transform Your Analytics?
Join teams already using AI to make data-driven decisions faster than
ever.
Software capitalization, client billing and R&D tax credits all run on Jira time logs that engineers fill in on Friday afternoon, from memory. Learn what engineering and finance leaders on Reddit say about time tracking nobody trusts, and how Keypup MCP compares Jira worklogs with linked pull requests, commits and merges to show when time is logged, how late it arrives, and which hours have code behind them.
Your pipelines got faster and your reviews got shorter, yet features still take months to reach customers. Learn what engineering leaders on Reddit say about work that waits weeks in Backlog, Ready for Development and To Do before anyone writes code, why delivery dashboards that start at the first commit miss it, and how Keypup MCP uses Jira workflow history, sprints and the commit and merge dates of linked pull requests to measure the queue in front of your code.
Green on the outside, red on the inside. Learn what engineering leaders on Reddit say about Jira tickets moved to Done before the code ships, velocity that doesn't match delivery and status reports nobody trusts at enterprise scale, and how Keypup MCP compares each issue's closure date, workflow history and sprint with the state and merge dates of its linked pull requests to measure how much of your reported "Done" has no merged code behind it.