Guía9 min

pnpm para coding agents: frozen-lockfile, nunca npm install

Resumen

En CI, pnpm install falla si el lockfile existe y habría que actualizarlo. Un agente que corre npm install crea package-lock.json y rompe el repo. Esta guía fija el contrato: pnpm i --frozen-lockfile, pnpm <script>, pnpm dlx; cero npm/npx. Docs oficiales de pnpm install.

GitHub
Un agente eligiendo pnpm con lockfile congelado frente a npm que genera otro lock

Qué resuelve

Esta pieza se queda en la decisión práctica: qué instalar, qué riesgo agrega y cómo aplicarlo sin romper operación.

Este sitio (y muchos repos de producto) usan pnpm solamente. Un coding agent que “instala dependencias” con npm install no es inofensivo: escribe package-lock.json, ignora pnpm-lock.yaml y deja un PR que no pasa CI. La doc de pnpm es explícita: en CI, pnpm install falla si hay lockfile y necesita update.

No sustituye sandboxing. Es el borde del package manager. El curso instalar un agente no cubre lockfiles.

El contrato en una línea

pnpm install --frozen-lockfile
pnpm test
pnpm exec tsc --noEmit
pnpm dlx some-oneoff

Prohibido: npm, npm i, npm ci, npm run, npx, crear o tocar package-lock.json.

pnpm i --frozen-lockfile no actualiza pnpm-lock.yaml. Si el agente cambió package.json, el install debe fallar: eso es correcto. Entonces pnpm add <pkg> (que sí actualiza el lock) en un commit consciente, no un npm i lodash de paso.

Lockfile congelado vs un segundo lock de npm

Qué dice pnpm install (11.x)

  • Alias: i.
  • En CI, si el lockfile está presente y habría que actualizarlo, falla.
  • --frozen-lockfile: no toca el yaml.
  • --lockfile-only: solo yaml + package.json, no node_modules.
  • --offline: solo store local; si falta el paquete, falla.
  • --dry-run (v11.8+): resuelve y reporta sin escribir.
  • --force: reinstalación agresiva. Un agente no lo usa “porque falló una vez”.
  • Workspace: pnpm install instala todos los proyectos salvo recursive-install=false.

pnpm ci existe como comando de CI (docs /cli/ci): trátalo como el primo de npm ci, no como excusa para invocar npm.

Por qué el agente se equivoca

  1. Entrenamiento con npm. El modelo default es npm i. Las instrucciones del repo tienen que decir pnpm en las primeras líneas (AGENTS.md / packageManager en package.json).
  2. npx vs pnpm dlx. npx puede bajar otra versión y ensuciar cache. pnpm dlx usa el ecosistema pnpm.
  3. Lockfile mixto. Git empieza a trackear package-lock.json. CI de pnpm y de npm pelean. El fix es borrar el lock de npm en el mismo PR, no “dejar ambos”.
  4. --prod sin lock. pnpm igual resuelve devDependencies para armar un lock consistente. No asumas que --prod salta resolución.

CI rojo porque el lockfile debía actualizarse

Receta para el harness

En AGENTS.md:

Package manager: pnpm only.
Install: pnpm install --frozen-lockfile
Add dep: pnpm add <pkg>   # actualiza lock
Scripts: pnpm <name>      # no npm run
One-off: pnpm dlx <pkg>
Never: npm, npx, package-lock.json

Si package.json tiene "packageManager": "[email protected]", Corepack puede pinnear. El agente no “actualiza pnpm global” con curl.

Worktrees: cada árbol necesita su pnpm install. No hagas symlink de node_modules hacia otro worktree: Next/Turbopack puede rechazar el symlink fuera del root.

FAQ

¿pnpm i a secas en local? Sí para humanos. El agente en CI/PR usa --frozen-lockfile salvo que el ticket sea añadir una dep.

¿Yarn? Si el repo es pnpm, no. Un lockfile de yarn es el mismo incidente.

¿El agente puede --no-lockfile? No. Es cómo se “olvidan” las versiones.

¿Store compartido? Es el diseño de pnpm. No lo borres con rm -rf ~/.local/share/pnpm porque un install falló.

Si el PR del agente solo “arregla CI” añadiendo package-lock.json al .gitignore, el daño ya está: alguien corrió npm. El revert es git rm -f package-lock.json y un pnpm install --frozen-lockfile verde. No hay tercera vía.

Verificado 2026-09-03 contra https://pnpm.io/cli/install (frozen-lockfile, CI fail, dry-run, offline).