Cómo evitar procesos huérfanos al ejecutar tests con Claude Code

02-09-2026

El problema que nadie te cuenta

Imagina esta situación. Estás trabajando con Claude Code en tu proyecto, le pides que ejecute los tests, todo pasa en verde y sigues programando tan tranquilo. Pasan las horas, abres el monitor de actividad y descubres que tu Mac está usando el 80% de la CPU, toda la RAM y por si fuera poco 7Gb extra de SWAP, sin razón aparente. ¿Qué está pasando?

El culpable silencioso son los procesos huérfanos de Vitest, Jest u otros test runners que se quedan ejecutando en modo watch cuando deberían haber terminado.

La causa raíz

Investigando el problema, descubrí que la causa no estaba en cómo Claude ejecuta los comandos, sino en qué comandos tiene permiso para ejecutar.

En el archivo .claude/settings.local.json de mis proyectos tenía configurados permisos como estos:

 

{ "permissions": { "allow": [ "Bash(npx vitest run:*)", "Bash(npx vitest:*)" ] } }

El problema está en npx vitest:*. Cuando Claude ejecuta npx vitest sin el flag --run, Vitest intenta entrar en modo watch. En un terminal normal detectaría que no hay TTY interactivo y terminaría, pero en el entorno de Claude a veces se queda como proceso huérfano consumiendo recursos indefinidamente.

Incluso npx vitest run:* es problemático porque vitest run (sin guiones) es una sintaxis ambigua que puede confundir a Claude sobre cómo ejecutar los tests.

La solución elegante

La solución es mucho más simple de lo que esperaba: usar siempre npm test en lugar de invocar el test runner directamente.

Si tu package.json ya tiene el script de test configurado correctamente:

 

{ "scripts": { "test": "vitest --run" } }

Entonces solo necesitas cambiar los permisos en .claude/settings.local.json:

 

{ "permissions": { "allow": [ "Bash(npm test:*)" ] } }

Esto garantiza que:

  1. Siempre se use el flag --run definido en tu package.json
  2. Los tests se ejecutan una vez y terminan limpiamente
  3. No quedan procesos huérfanos

Cómo verificar que funciona

Después de hacer el cambio, puedes comprobarlo ejecutando tests varias veces seguidas y luego buscando procesos huérfanos:

 

ps aux | grep -E "vitest|jest|mocha" | grep -v grep

Si la configuración es correcta, no deberías ver ningún proceso. Los test runners terminan limpiamente sin dejar zombies consumiendo recursos.

También puedes probar ejecutando tests en paralelo para asegurarte de que incluso bajo carga todo termina correctamente:

 

npm test & npm test & wait ps aux | grep vitest | grep -v grep

Revisando todos tus proyectos

Si trabajas con múltiples proyectos, es probable que tengas el mismo problema en varios de ellos. Puedes encontrar todos los archivos de configuración de Claude con:

 

find ~/Dev -name "settings.local.json" -path "*/.claude/*"

Y buscar cuáles tienen permisos problemáticos:

 

grep -l "npx vitest" $(find ~/Dev -name "settings.local.json" -path "*/.claude/*")

En cada archivo encontrado, reemplaza:

  • "Bash(npx vitest run:*)""Bash(npm test:*)"
  • "Bash(npx vitest:*)""Bash(npm test:*)"

Conclusión

A veces los problemas más molestos tienen las soluciones más simples. No necesitas scripts de limpieza, hooks complejos ni matar procesos manualmente. Solo necesitas asegurarte de que Claude ejecute los tests de la forma correcta desde el principio.

La clave está en usar npm test que respeta la configuración de tu proyecto, en lugar de invocar el test runner directamente con comandos que pueden quedarse en modo watch.

Revisa tus permisos, haz el cambio una vez en cada proyecto, y olvídate de los procesos huérfanos para siempre.


Resumen de cambios necesarios

En cada proyecto, editar .claude/settings.local.json:

 

- "Bash(npx vitest run:*)", - "Bash(npx vitest:*)", + "Bash(npm test:*)",

Asegurarse de que package.json tenga el flag --run:

 

{ "scripts": { "test": "vitest --run" } }