AI coding

Estimating a Dependency Change Before an Agent Installs

Antonio Evans10 min read

A split scene: on the left, a terminal showing a lockfile diff with added and removed lines highlighted, and a callout counting direct and transitive packages; on the right.
Key points

Key points

  1. 1Run the install in dry-run mode first and diff the resulting lockfile against the committed one to see the real dependency churn before any write happens.
  2. 2Count changed entries and separate direct updates from transitive ones, because reviewers spend different amounts of attention on each.
  3. 3Multiply changed packages by a review rate measured from your own past reviews (minutes per changed package) to get an expected reviewer time.
  4. 4Split a dependency task into two runs when the change crosses either of two thresholds: a large line count in the lockfile diff, or a wide blast radius across packages and majors.

Why a dependency change needs a preflight estimate

An agent that runs an install is making a change to your software supply chain. A lockfile is the machine-readable record of the exact versions resolved for every direct and transitive dependency, and it exists to make builds reproducible and to let you verify the integrity of what gets installed (Pants on lockfiles). Once an install runs and you commit the resulting lockfile, you ship whatever the resolver picked, including packages you never named.

The failure mode is not that installs break. It is that installs succeed and quietly change a lot. The distinction between the manifest (what you asked for) and the lockfile (what you got) is exactly where drift enters, and drift can produce non-reproducible builds, silent security regressions, and inaccurate SBOMs because most SBOM tooling reads the lockfile to determine what is actually installed (sbomify on lock file drift). An agent that runs npm install instead of npm ci will regenerate the lockfile rather than fail on a mismatch, which is how a deployment ends up with versions nobody tested.

Lockfiles also follow very different design decisions across package managers, which matters when you write estimation logic: npm, pnpm, Cargo, Poetry, Pipenv, Gradle and Go all generate and evolve their lockfiles differently, and a study of that design space found developers commonly struggle with maintaining and interpreting them (the lockfile design study). Any estimator you write has to be per-ecosystem, not generic.

Safety gates for agents are usually framed around permissions, intent confirmation and argument validation (the 12 preflight checks). Those gates decide whether a tool call is allowed. This article adds the missing gate for installs: whether the change is small enough to hand to a reviewer in one piece.

Reading the lockfile diff: direct versus transitive

The procedure is the same in every ecosystem:

  1. Start from a clean tree with the committed lockfile.
  2. Run the package manager's dry-run resolution and capture the would-be lockfile to a separate path.
  3. Diff the two files.
  4. Classify each changed package as direct (listed in the manifest) or transitive (pulled in).
  5. Record the version delta: patch, minor or major.

For npm you can write the candidate lockfile without touching the working one:

# resolve into a scratch copy, then diff
cp package-lock.json /tmp/lock.before.json
npm install --package-lock-only --ignore-scripts
cp package-lock.json /tmp/lock.after.json
git checkout -- package-lock.json
diff -u /tmp/lock.before.json /tmp/lock.after.json | grep '^[+-]' | wc -l

The --package-lock-only flag updates only the lockfile and skips lifecycle scripts, so nothing executes. For Python, poetry lock --no-update style workflows or a pip-compile run into a scratch output achieve the same thing, and Pants users generate lockfiles explicitly with pants generate-lockfiles (Pants lockfile docs).

The classification step is where teams usually skip work and pay for it later. Count four numbers per run:

MetricHow to get itWhy it matters
Direct packages changedManifest names whose resolved version movedThese are the ones you can explain to a reviewer
Transitive packages changedEvery other changed entry in the lockfileThe hidden bulk of the review surface
Major version jumpsVersion segment 1 differsHighest chance of behavioral change
Added or removed packagesEntries present in only one fileNew supply chain surface, or dead code path

A package manager will happily pull in a patched transitive version when you widen a direct range. A pinned direct dependency can hold a transitive package at a version that has a known vulnerability, and widening to a caret range can allow the patched version through instead (HeroDevs on transitive resolution). So a lockfile diff showing "one direct package changed" can still be twenty entries of churn. Count them.

One more thing worth knowing before you trust the count: passing a test in the agent's own workflow is weaker evidence than it looks. An analysis of repair-agent validation found that 46.0% of positive comparable validation events carried no bug-discriminating information, and 23.8% of baseline rollouts closed on evidence of that kind (Xu and Wu, validation evidence in repair agents). Apply the same skepticism to "the agent ran the test suite and it passed" after a dependency bump.

Turning churn into reviewer minutes

Reviewer time is not linear in a useful way, but it does have measured anchors. An analysis of more than 50,000 pull requests across 200+ engineering teams reports that each additional 100 lines changed adds about 25 minutes of review time, that defect detection falls from 87% for 1 to 100 line changes to 28% above 1,000 lines, and that PRs over 1,000 lines have roughly 70% lower defect detection than smaller ones (the PR size study). Those are the study's own numbers, not universal constants, but they give you a shape: cost rises roughly linearly and quality falls faster.

Your review rate should come from your own history. Two ways to measure it:

  • Per changed package. Take your last ten dependency-only pull requests, note the number of changed lockfile entries and the time from first review request to approval. Divide total minutes by total changed packages. If ten PRs covered 240 changed packages and took 600 reviewer minutes, your rate is 2.5 minutes per changed package.
  • Per 100 diff lines. Use the 25 minute figure from the study above as a starting prior and overwrite it with your own median.

Then the estimate is one multiplication:

expected_reviewer_minutes = changed_packages * minutes_per_changed_package

A worked example. A dry run shows 4 direct packages changed and 31 transitive packages changed, so 35 changed entries. At a measured rate of 2.5 minutes per changed package, that is about 88 minutes, so plan for roughly two reviews rather than one. The same diff in raw lockfile lines is often several hundred, because each entry carries an integrity hash and resolved URL; if the diff is 900 lines, the 25 minutes per 100 lines prior puts the estimate at 225 minutes. The two methods disagree by a factor of more than two, which is itself informative: line counts in lockfiles are noisy, so use changed packages as the primary estimate and line count only for splitting decisions.

Add a fixed overhead for the run itself. A clean install and test cycle is usually 5 to 20 minutes of wall clock, and the agent's own iteration should be bounded. The estimate you present to whoever approves the task is reviewer minutes plus bounded run time, not "it's just a version bump."

The split rule: when one task becomes two runs

Here is the decision rule I recommend, and it is deliberately simple enough to put in a prompt.

Split a dependency task into two runs when either condition holds:

  • Size: the dry-run diff changes more than 20 lockfile entries, or more than 400 diff lines.
  • Blast radius: any major version jump on a direct package, or two or more direct packages changing in the same run, or any added or removed package that is not a direct dependency.

When neither holds, run it as one task. When the rule fires, run one pass that updates direct dependencies only with transitive resolution frozen where the package manager allows it, verify, merge, and then run a second pass for the remaining transitive updates.

The 400 line threshold is not arbitrary. The PR size study places the best defect detection in the 200 to 400 line band, with 87% detection below 100 lines and a steep decline past 600 lines (the PR size study). A dependency change of 400 lines already sits at the top of the useful band, and 600 lines is past the point where reviewers stop catching things. The 20 entry threshold is the rough equivalent of 400 lines given that lockfile entries average several lines each; recalibrate it against your own diff sizes.

The blast radius clause is the more important half. Size tells you how much reading there is. Blast radius tells you how much can go wrong and how hard it will be to attribute a failure. If two direct packages change in one run and the test suite goes red, you have to bisect the dependency change to find which one. Split into two runs and each failure has one suspect.

There is also a workflow reason to keep runs isolated: an agent doing a dependency change wants a clean workspace with no half-finished feature edits, and git worktrees give you exactly that, multiple working directories attached to one repository (git-worktree documentation). One worktree for the dependency run, the feature worktree untouched, and a third for review if you want it (a practical worktree setup). Parallel agents each get their own workspace rather than fighting over one checkout (Laurent Kempé on worktrees for agents).

Putting the estimate into the preflight prompt

The estimate is only useful if it runs before the write. Put it in the same place as your other tool gates, where the agent is allowed to propose and the gate decides whether to execute (the preflight checks). A prompt fragment that works:

Before running any package install:
1. Run the resolution in dry-run mode into a scratch file. Do not modify the lockfile.
2. Diff scratch lockfile against the committed one.
3. Report: direct_changed, transitive_changed, major_jumps, added_removed, total_changed, diff_lines.
4. Compute expected_reviewer_minutes = total_changed * RATE, where RATE comes from team config.
5. If total_changed > 20 OR diff_lines > 400 OR major_jumps > 0 OR direct_changed > 1,
   stop and propose a two-run split instead of installing.
6. Otherwise install, run the full test suite, and paste the counts and the estimate into the PR description.

Two details make this robust. First, every number in the report must be produced by a command the agent actually ran, not generated from memory; structured output with explicit fields reduces the chance the model invents a count. Good prompt practice for structured and verifiable outputs is well documented (OpenAI prompt engineering guide). Second, the rate is configuration, not something the model should guess: store it in the repository next to your CI config.

If you want the agent's own reasoning about the diff to be cheaper or more consistent across runs, a single inference endpoint for the summarization step keeps the plumbing simple; Quanta Inference puts text, image, video, audio and embedding models behind one OpenAI-compatible key at https://quantaintelligence.ai/inference, which is handy when the same preflight runs against different models over time.

For the split condition, resist the temptation to let the agent choose. A model that has been asked to install a package has an incentive to describe the change as small. Make the gate deterministic: the numbers come from a script, the threshold comparison happens in code, and the agent only reports the outcome. That mirrors why preflight checks exist at all, which is that most failures happen before execution, in the decision, not in the tool call.

Worked example: two estimates, one decision

Consider a Node project with a manifest that pins three direct dependencies exactly. The task: "update dependencies to pick up patches."

Dry run one. The lockfile diff shows 3 changed entries, 2 of them transitive, no major jumps, 140 diff lines. At 2.5 minutes per changed package that is about 8 minutes of review. The rule does not fire. Run it as one task, install, test, open one pull request with the counts in the description.

Dry run two. The same manifest but a wider range set, which lets the resolver move more. The diff shows 6 direct packages changed, 47 transitive, 2 major jumps, 1 added package, 1,300 diff lines. At 2.5 minutes per changed package, that is about 133 minutes of review, and at the line-count prior it is far worse. The rule fires twice over. Split: first run updates the direct packages with a frozen transitive set where possible (6 direct changes is still a big first run, so split again by package if the tests are shaky), verify and merge; second run takes the transitive churn. Each run lands under the thresholds, each failure has a small suspect set, and no single review exceeds the useful band.

The estimate also gives you something to say to stakeholders. "This is 53 packages and roughly two reviewer hours" is a decision. "It's just a version bump" is not.

If your team lacks the measurement history to set a rate, or you want the preflight built and calibrated rather than hand-rolled, that is the kind of thing a Quanta expert can do against your own repository history; the expert reads your context and hands back finished deliverables, including the script and the prompt fragment, at https://quantaintelligence.ai/agents.

Common mistakes that inflate the estimate

  • Counting raw diff lines as work. Every changed lockfile entry carries an integrity hash and a resolved URL, so a one version bump can be five or six diff lines. Count packages first.
  • Treating npm install and npm ci as equivalent. npm ci installs exactly what the lockfile specifies and fails on a mismatch, which is why it is the recommended CI command (sbomify on drift detection). Use it to prove the dry-run result installs cleanly.
  • Ignoring that patch bumps do not reach everything. An auto-merge bot only acts where a newer patch release exists for a direct dependency, and does nothing for deeply transitive packages, end-of-life libraries, or findings a scanner marks as having no fix available (Seal Security on patch auto-merge). Those are exactly the cases where the split rule matters.
  • Letting the lockfile drift between estimate and install. If the tree changes after the dry run, your numbers are stale. Re-run the preflight immediately before the install.
  • Assuming a green suite means the change is safe. Evidence quality is a separate question from evidence presence, as the repair-agent analysis shows (Xu and Wu). A suite that passes on the old and new code tells you less about a dependency change than you would like.

A useful sanity table for the decision:

SituationEstimateAction
Under 20 changed entries, no majors, one direct packageUnder 50 reviewer minutesSingle run
20 to 40 entries, no majors, up to two direct50 to 100 minutesSingle run with a named reviewer
Over 20 entries or over 400 diff linesOver 50 minutes, quality riskSplit by direct then transitive
Any major jump on a direct packageUnboundedSplit, and pin the old version for rollback
Added package not directly declaredUnknown surfaceInvestigate before installing
FAQ

FAQ

How do I count transitive updates without reading the whole lockfile?

Diff the committed lockfile against a dry-run resolution and parse both as structured data, then subtract the manifest's direct names from the set of changed entries. Every remaining changed entry is transitive. This is mechanical in npm, Poetry, Cargo and Pants-style lockfiles because they are structured formats rather than prose.

What review rate should a team start with if it has no history?

Start with the 25 minutes per 100 lines changed figure reported in the PR size study, then replace it within a month with your own median per changed package from the last ten dependency pull requests. The published figure is a prior, not a standard, and your ecosystem's lockfile verbosity will shift it.

Does splitting a dependency task into two runs cost more total time?

It usually costs slightly more in agent run time and two round trips of review latency, and it saves more in reviewer focus and failure attribution. The PR size data shows that past roughly 600 changed lines, defect detection drops sharply, so a split that keeps each run in the useful band tends to pay for itself the first time a test goes red and only one package could be responsible.

Should patch-level updates skip the estimate entirely?

No. Patch-level updates are the safest tier, but the semver contract behind a patch tag is maintained by convention rather than enforced, and patch bumps cannot reach transitive packages inside an unchanged direct dependency (Seal Security). The estimate is cheap to compute and tells you whether the transitive resolution moved even though the direct package changed only in its third digit.

How does this interact with parallel agent worktrees?

Run each dependency estimation pass in its own worktree so the dry run and the eventual install happen against a tree nobody else is editing. Worktrees share one git history across multiple working directories (git-worktree documentation), so a dependency run costs disk space for one checkout, not one clone, and your other agents keep their workspaces stable while the preflight runs.

Sources

Sources

  1. Lockfiles
  2. What Is Lock File Drift? A Hidden Risk in Dependency ...
  3. The Design Space of Lockfiles Across Package Managers
  4. Agent Safety Gates: 12 Preflight Checks Before Tools Run
  5. Securing Transitive Dependencies in End-of-Life Software
  6. Validation Evidence in LLM Repair Agents
  7. The Impact of PR Size on Code Review Quality: What Data ...
  8. Git - git-worktree Documentation
  9. Set up a local repository in which every branch is checked ...
  10. Laurent Kempé - From 3 Worktrees to N: How AI Agents ...
  11. Prompt engineering | OpenAI API
  12. Should You Auto-Merge Patch-Level Dependency Updates ...
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