Practical guide · AI coding guardrails
Why AI coding agents add the wrong lockfile — and how to stop it
A team chooses pnpm. The repository has pnpm-lock.yaml. Then an AI coding tool is asked to add a dependency, runs npm install, and quietly commits package-lock.json. The feature works, the pull request looks normal, and the repository now contains two different answers to the same question.
Published 18 September 2026
The decision
Your repo already chose npm, pnpm, Yarn or Bun.
The drift
An agent runs a different installer and creates a second lockfile.
The risk
CI, developers and agents can now resolve different dependency trees.
The common failure
“Use pnpm” is useful guidance. It is not enforcement.
Teams increasingly put rules in CLAUDE.md, AGENTS.md and similar instruction files. That helps, but instructions remain context: they can be missed, contradicted by another file, or simply lose to a tool invocation that seems locally reasonable.
The package-manager problem is especially easy to create because all four common JavaScript package managers are designed to make dependency installation easy. If npm happens to be available, npm install some-package may succeed immediately even when the repository has already standardized on pnpm.
Public AI-development discussions now describe exactly this pattern: teams write “pnpm only” rules, then add hooks or CI checks because documentation alone still lets the occasional npm artifact through.
What actually breaks
Two lockfiles can mean two different dependency graphs
Before
package.json
"packageManager": "pnpm@10.15.0"
pnpm-lock.yaml
One package manager. One dependency-resolution history.
After one wrong install
package.json
"packageManager": "pnpm@10.15.0"
pnpm-lock.yaml
package-lock.json ← new
Now different tools can install from different lockfiles.
The result is not necessarily an immediate outage. It is worse in a different way: the repository can appear healthy while developers, CI jobs and coding agents stop sharing exactly the same dependency state. That creates bugs that reproduce on one machine and disappear on another.
A stronger pattern
Put the decision in the repo, then check every pull request
1. Declare it
Use the packageManager field in package.json and keep one canonical lockfile.
2. Tell the agents
Repeat the rule in CLAUDE.md or AGENTS.md so the agent knows what to do before it acts.
3. Enforce it
Run a deterministic pull-request check so a second lockfile cannot slip through unnoticed.
Free guardrail
CrossCheck catches the contradiction in the pull request that introduces it
CrossCheck is a free, MIT-licensed command-line tool and GitHub Action from zFinia. It checks package-manager configuration, points to the conflicting file, and stays advisory unless you choose to block new contradictions. It runs inside your own repository and does not upload your source code to zFinia.
name: CrossCheck
on: pull_request
jobs:
crosscheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: zFinia/crosscheck@v0
What not to do
Do not solve a deterministic problem with another vague instruction
- • Do not keep multiple lockfiles “just in case” unless different packages genuinely use different managers.
- • Do not assume reviewers will notice a new generated lockfile inside a large AI-generated pull request.
- • Do not rely on a README sentence as the only control.
- • Do not block every pull request for old repository debt; flag what the new change introduced.
- • Do not use an LLM to decide a rule that can be checked deterministically from files already in the repository.
FAQ
Can package-lock.json and pnpm-lock.yaml exist in the same repo?
Yes, especially in monorepos where separate packages deliberately use different managers. The problem is when both lockfiles describe the same package while the repository otherwise establishes one canonical manager.
Will CrossCheck stop Claude Code, Codex or Cursor from running npm?
CrossCheck is a pull-request guard, not a runtime command blocker. It catches the conflicting repository state. Teams that also want to stop the command before it runs can pair it with agent hooks or shell-policy controls.
Does CrossCheck inspect private source code on zFinia servers?
No. The scanner runs on your machine or in your own GitHub Actions job. Its package has no runtime dependencies and makes no network calls while scanning.
Sources and examples