AI coding

When an Agent Bumps a Major Version: A Pre-Merge Checklist

Antonio Evans9 min read

A split scene in a software team's war room.
Key points

Key points

  1. 1A semver-major bump in a dependency is a behavior change, not a version change, so review it the way you would review refactored code you did not write.
  2. 2The visible diff is often one line, but the blast radius covers runtime behavior, peer dependency resolution and transitive type changes.
  3. 3Treat every breaking item in the changelog as a checklist row that needs either a matching code change or a passing targeted test.
  4. 4Read the lockfile diff before anything else, because an agent may upgrade transitive packages it never mentioned.

Why the diff is one line but the risk is not

An agent asked to "update dependencies" will often change a single string. In package.json it turns "left-pad": "^2.4.1" into "^3.0.0", or in pyproject.toml it raises a pin. Git shows one removed line and one added line. A reviewer scanning the diff sees nothing to argue about.

That framing is wrong for a major version. Semantic Versioning exists precisely to signal that the public API changed: the spec says the major version is incremented when incompatible API changes are introduced, and that minor and patch versions reset to zero when that happens the SemVer 2.0.0 specification. So a major bump is the dependency author telling you, in the version string, that code written against the old version may no longer work. The version number is a warning label, not a change description.

An agent that edits only the version string has done the mechanical step and none of the judgment step. The judgment step is yours. The rest of this article is the sequence to run before you click merge.

One useful prior: reviewers should approve a change once it definitely improves overall code health, even if it is not perfect, and should not wave through changes that worsen it except in an emergency Google's review standard. A major bump with unread breaking changes is a change of unknown quality, so it does not meet that bar yet.

First, confirm the agent did one thing and did it honestly

Before reading release notes, establish what exactly changed. Scope drift is the most common agent failure mode in review: the agent edits files the task never named, and the Open Worldwide Application Security Project calls missing these edits the most common review failure mode in agentic coding SpecStory's review checklist for AI code.

Run these three commands before opening the changelog:

git diff --name-only --no-renames origin/main...HEAD
git diff origin/main...HEAD -- package.json package-lock.json
git log --oneline origin/main..HEAD

What you want to see: manifest and lockfile only. If the diff also touches source files, test files, CI configuration or lint rules, stop and ask why. An agent that "fixes" a build by weakening an assertion or disabling a lint rule has not upgraded anything; it has moved the failure. The same checklist flags test tampering (swapping toHaveLength(2) for toBeDefined()) and quality gate changes as pre-screening items SpecStory.

Also confirm the package is the right one. Models can invent package names, and attackers register names that resemble what a model might hallucinate, a supply-chain pattern sometimes called slopsquatting Metacto's AI code review standards. If the PR adds any new package alongside the bump, verify each name on the canonical registry first.

Read the changelog and write the breaking list down

Open the changelog and the migration guide, and produce a written list. Not a mental list. A table in the PR description or a comment, where each breaking item gets a row. The format that works:

Breaking itemWhat it changesCode in our repo that uses itAction takenTest that covers it
Function renamed parse to parseAsyncReturn type becomes a promisesrc/lib/import.ts lines 40-58Calls wrapped in await, callers updatedimport.test.ts async cases
Default export removedMust use named importsrc/jobs/sync.ts, src/jobs/report.tsSwitched to named importsync.test.ts
Node engine raisedRuntime floor changesCI matrix, DockerfileBumped base image, updated matrixCI run on branch

Two things happen when you write this down. First, rows with an empty "Action taken" column are your to-do list, and any row that stays empty at merge time is grounds to reject the bump. Second, rows with an empty "Test that covers it" column tell you where to add a targeted test, even if the code change looks trivial.

This is the same instinct behind Google's review guidance to check that tests actually fail when the code beneath them breaks, that they make simple useful assertions, and that test complexity is not accepted just because tests are not part of the main binary what to look for in a code review.

If the changelog is silent on something you depend on, the source is the changelog you have. Read the release notes for every major version between the current one and the target, not just the last one. A jump from 2 to 5 has three sets of breaking notes.

Grep for every removed or renamed symbol

This step is boring and it finds the bugs. For each name on your breaking list, search the whole repository, including tests, scripts, docs, config files and infrastructure code.

# removed or renamed functions, classes, options, env vars, CLI flags
rg -n --hidden -g '!node_modules' -g '!.git' \
  'oldFunctionName|OldClassName|oldOptionName|OLD_ENV_VAR'

# default-import usage that a named-export change would break
rg -n "import \w+ from ['\"]the-package['\"]"

# deep imports, which break most often across majors
rg -n "the-package/(internal|lib|dist)/"

Deep imports matter more than they look. Libraries frequently change their internal file layout in a major release, and an import from a package subpath that is not part of the declared public API is exactly the kind of thing the SemVer spec puts outside the compatibility promise, since the spec is built around a declared public API semver.org. Deep imports are also where an agent's own generated code tends to reach, because a model reproduces patterns it saw in training rather than the API surface your version actually exposes.

Record the grep hit count per symbol next to your breaking list. Zero hits and a deprecated-but-working API is a very different risk than zero hits and a removed API from a code path that only runs in production.

Resolve and inspect the peer dependency output

A major bump rarely lands alone. Peer constraints and transitive resolutions shift, and the checker output tells you what the resolver actually did.

For npm, npm ls lists installed dependencies and exits non-zero when the resolved tree in node_modules does not match what package.json should resolve using npm ls. Add --all to see transitive dependencies rather than only the direct ones viewing an npm dependency tree.

npm ci
npm ls --all > /tmp/tree-after.txt
npm ls <the-package>          # who pulls it in, and at what version
npm ls --all 2>&1 | grep -i "invalid\|missing\|UNMET" || echo "clean"

For Python, pip check reports installed packages with broken or conflicting requirements, and pip install output shows what got upgraded as a side effect. For a compiled workspace, cargo tree -d shows duplicate crate versions and cargo tree -i <crate> shows which dependents pulled a crate in. Whichever ecosystem you use, the question is the same: did the resolver have to duplicate or drop a package to satisfy the new constraint, and who else is affected.

Peer conflicts are where teams get tempted to reach for overrides. npm overrides force a version across the tree, and the tooling exists for exactly that, but forcing a version that a peer explicitly excludes turns a loud install error into a quiet runtime bug a guide to npm overrides. If the bump only installs with an override, that is a decision for a human with a written reason, not a side effect of an agent's PR.

The dependency count also tells you how much surface you just inherited. As a rough scale, a zero-dependency state library adds one package, Express adds dozens, and a full framework install can add hundreds of transitive packages npm dependency tree depth. A major bump on a package three levels deep moves everything beneath it.

Hardening basics still apply after a bump: deterministic installs from the lockfile, no unverified registries, and an audit pass are baseline practices for supply chain risk, which is not going away npm security best practices.

Read the lockfile diff for upgrades the agent did not mention

This is the step reviewers skip most often, and it is the one the agent's one-line PR description actively hides. The lockfile diff shows every resolved package that changed, including transitive ones nobody asked about.

# lockfile only, grouped by package, ignore pure hash and resolved-URL churn
git diff origin/main...HEAD -- package-lock.json | grep -E '^[+-]\s+"(version|node_modules)' | head -100

Read it as a list of surprises. A lockfileVersion churn or registry URL change is noise. A transitive package crossing a major version because your bump widened its range is a second review you did not plan for. Research on AI-generated code repeatedly finds that a single flawed pattern gets replicated at scale, and omission of necessary controls is a common failure mode rather than overtly wrong lines Cloud Security Alliance on AI code risks. A dependency bump that quietly drags in an unmaintained or vulnerable transitive package is the same failure shape: nothing looks wrong, and something is.

Evaluation work backs the general point. In one study across five models, almost half of the generated snippets contained bugs that were often impactful, and code generation benchmarks largely test functional correctness rather than security CSET's report on cybersecurity risks of AI-generated code. Published analyses of AI code put the share with security or design flaws much higher Security Journey's summary of AI code risks and Endor Labs on common vulnerabilities. You do not need to accept the exact percentages to act on the pattern: generated changes read plausibly and need a check that does not depend on them being plausible.

Run the full suite plus targeted tests for the touched surface

The full suite is the floor, not the finish. A major bump can pass a suite that never exercises the changed surface, and when an agent wrote or edited the tests in the same PR, a green run proves less than usual. Google's guidance is explicit that a human must confirm tests are valid and would actually fail when the code breaks eng-practices.

A practical sequence:

  1. Type check or compile with no emit. In TypeScript, tsc --noEmit catches type changes that a test runner in transpile-only mode will happily ignore, and running it before tests in CI is the standard safety net for dependency PRs typescript.tv on dependency update automation.
  2. Full suite on a clean, deterministic install. npm ci rather than npm install, so you test the lockfile and not whatever the resolver felt like doing.
  3. Targeted tests per breaking-list row. Each row names a test. If the test does not exist, add it in the same PR, which is the normal expectation for production code and its tests together Google's review guidance.
  4. One end-to-end run of the workflow that uses the dependency most. A record of the changed workflow running after the last edit is on the standard agent-code checklist for good reason SpecStory.
  5. Check the failure paths, not just the happy path. Tests for malformed input, empty states and permission failures belong in any change that can alter behavior at a boundary Metacto.

The cost is roughly proportional to blast radius, not diff size. A one-line bump of a package you call from two files, with a clean npm ls, no peer conflicts, and three targeted tests, is thirty minutes. The same one-line bump of a package behind your HTTP client or your serialization layer is a half day and worth every minute.

For teams running bumps across many services at once, the review effort is the bottleneck, not the edit. That is the natural place where a shared model access layer with predictable pricing helps, since review triage, changelog summarization and grep-targeted test generation are all text jobs you can run through one API: Quanta Inference.

The decision rule: merge, fix or pin

At the end of the checklist you have facts. Turn them into one of three outcomes with a rule you can apply the same way every time.

OutcomeConditionsWhat you do
MergeEvery breaking item has a code change and a targeted test; peer resolution is clean or the override is documented; lockfile diff has no unexplained majorsApprove after the full suite and one end-to-end run pass
Merge with follow-upBreaking items are covered; a minor behavior change is noted but no consumer depends on it yet; a transitive major is understood and trackedApprove, file the follow-up ticket in the PR
Reject and pinAny breaking item without a code change or test; install only succeeds with an undocumented override; lockfile shows unexplained major upgrades; tests were weakened to passClose the PR, pin the current major, and require the agent or a human to redo it against the migration guide

Two supporting observations. First, "reject and pin" is not a failure, it is the correct answer whenever the evidence is incomplete, and pinning is cheap compared with a production incident. Second, "the tests pass" is not one of the conditions, because a suite that was not written for the changed API passing is the default outcome, not evidence.

If the team cannot agree on the outcome, escalate rather than letting the PR sit. The conflict-resolution guidance in Google's practices is to try for consensus, then escalate to a broader team discussion or a maintainer, and to record the result on the change for future readers Google's review standard. A stalled dependency PR decays: the next agent run rebases it, the changelog link rots, and the review starts over.

Whether a bump should change your own package's version number is a separate question and a perennial one; the SemVer maintainers' own discussion of dependency-driven releases notes that it depends on whether your public API changed, not on whether a dependency's did semver issue 148, with the broader community debate in this versioning discussion. Do not let it block the review of the bump itself.

For teams where agent-opened PRs arrive faster than humans can triage them, the pattern that works is a reusable review procedure encoded once and applied by a coding agent that follows it: Quanta Skills.

FAQ

FAQ

What is the first thing to check on an agent's major version bump PR?

Check that the diff is limited to the manifest and lockfile, then read the lockfile diff. If source files, tests, lint configuration or CI files also changed, investigate before reading any changelog, because the agent may have widened the scope or weakened a check to make the install pass.

How do I know which parts of my code a breaking change affects?

Build a written breaking list from the changelog and migration guide for every major version between the current and target release, then grep for each removed or renamed symbol across source, tests, scripts and config. Close the loop by naming a test for each row, since a row without a test is an unverified assumption.

The full test suite is green, is that enough to merge?

Usually not. A green suite means the tests you have still pass, and it says nothing about behavior your tests never covered, especially if the PR also edited tests. For each breaking item, confirm a targeted test exercises the new behavior, and run one end-to-end pass of the workflow that depends on the package most.

A peer conflict appeared after the bump. Should I add an npm override?

Only with a written reason and a named owner. Overrides force a version across the resolution tree, and forcing one that a peer explicitly excludes converts a loud install error into a silent runtime bug. If the bump cannot install cleanly without an override, that is a signal to reject and pin, not a routine fix.

When should I reject the bump instead of fixing it in the PR?

Reject when any breaking item has no corresponding code change or test, when the install only succeeds through a weakened check, or when the lockfile shows major upgrades the agent did not mention. Closing the PR and pinning the current major costs one round of rework; merging an unread major bump costs an incident and a rollback under pressure.

Sources

Sources

  1. Semantic Versioning 2.0.0 | Semantic Versioning
  2. The Standard of Code Review | eng-practices - Google
  3. Code review checklist for AI-generated code - SpecStory
  4. AI-Generated Code Review Checklist: 2026 Standards for Teams - Metacto
  5. What to look for in a code review | eng-practices - Google
  6. Using npm's ls command for Fun and Insight
  7. How to view the dependency tree of a given npm module?
  8. A Guide to NPM Overrides: Take Control of Your ...
  9. npm Dependency Trees: Most Nested Packages 2026
  10. Collection of npm package manager Security Best Practices
  11. Understanding Security Risks in AI-Generated Code | CSA
  12. Cybersecurity Risks of AI-Generated Code | Center for Security ... - CSET
  13. The Security Risks of AI-Generated Code: Common Risks & Fixes
  14. The Most Common Security Vulnerabilities in AI-Generated Code
  15. Never Review a Dependency Update Again
  16. Version after dependency changes? · Issue #148 - semver
  17. Best practices for versioning project after dependency ...
About the author

About the author

Antonio Evans
Antonio Evans

Antonio Evans is the founder of Quanta Intelligence and a fractional CTO who builds HIPAA-ready AI systems for health and life-science companies. Before that he was technical co-founder of FriendsEAT for twenty years, CEO of Socialdraft, and a data engineer at IBM. Antonio lives with his dog Santino in Lisbon, Portugal.

All articles