Permission budget de Claude Code: permisos, coste y logs en 5 minutos
Un loop práctico para reglas allow/deny, límites de coste, logs de ejecución y handoff en Claude Code.
Por qué revisar cinco minutos cada mañana
La pregunta operativa con Claude Code no es solo si puede programar. La pregunta es qué puede hacer sin detenerse a pedir permiso. Aprobar cada comando Bash parece prudente al principio, pero después de muchas ventanas de aprobación aparece la fatiga. Permitirlo todo es peor: lectura de .env, instalación de paquetes, git push, deploys de producción, migraciones y cambios de billing quedan demasiado cerca.
Un permission budget es una tabla corta de operación. Define qué acciones puede ejecutar Claude Code sin aprobación, cuáles deben preguntar a una persona y cuáles quedan prohibidas en ese repositorio. El loop es el hábito de revisar esa tabla cada mañana contra el log de ejecución y el uso del día anterior. En otras palabras: contar las llaves y el presupuesto que se le entregaron al agente.
Según la documentación oficial revisada el 3 de junio de 2026, Claude Code usa reglas allow, ask y deny; deny tiene prioridad sobre ask y allow. /permissions muestra las reglas activas y el archivo de settings de origen. Para coste, /usage sirve como visión local o de sesión, mientras Claude Console es la fuente para facturación y límites de workspace. Consulta Configure permissions, Claude Code settings, Manage costs effectively y la CLI reference.
Como lectura interna, enlaza este flujo con la guía de permisos de Claude Code, la checklist de auditoría de permisos y el permission receipt pattern.
Los conceptos básicos sin jerga
Los permisos de Claude Code los aplica la CLI, no la buena intención del modelo. Es útil escribir en CLAUDE.md “no leas secretos”, pero eso no es una barrera. Una regla deny como Read(./.env) o Read(./secrets/**) sí es una barrera que Claude Code puede aplicar.
Los modos también importan. default es el flujo normal de aprobación. plan favorece lectura e investigación. acceptEdits facilita cambios de archivos. dontAsk deniega las herramientas que no estén preaprobadas o marcadas como ask. bypassPermissions, también disponible como --dangerously-skip-permissions, salta los prompts y debe reservarse para contenedores o máquinas virtuales aisladas.
El coste se gestiona igual. /usage ayuda a detectar una sesión local inesperadamente cara, pero la facturación API debe confirmarse en Claude Console. En ejecuciones con claude -p, --max-budget-usd y --max-turns son límites útiles; no sustituyen a un presupuesto de equipo.
El loop diario de permission budget
La rutina debe ser lo bastante pequeña para repetirse. El objetivo no es una auditoría perfecta, sino no empezar el día con una autorización peligrosa abierta.
| Paso | Qué revisar | Condición de paso |
|---|---|---|
| 1 | /permissions | Sin Bash(*) ni reglas demasiado amplias como Bash(npm *) |
| 2 | .claude/settings.json | secrets, deploys, base de datos y billing están en ask o deny |
| 3 | /usage y Console | El gasto de ayer tiene explicación |
| 4 | git diff y log | El trabajo aprobado coincide con el diff |
| 5 | nota de handoff | Permisos abiertos, acciones bloqueadas y próximo revisor están escritos |
Prompt breve de inicio:
Before starting today's Claude Code work, classify the task into:
1. safe to run without approval
2. requires human approval
3. should not run in this session
Then list up to five checks for /permissions, /usage, and git diff.
Starter compartido de settings.json
Este ejemplo de .claude/settings.json sirve como punto de partida. defaultMode: "dontAsk" es estricto: lo que no esté permitido o marcado como ask-first no se ejecuta. Pruébalo en local antes de convertirlo en regla compartida.
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"defaultMode": "dontAsk",
"allow": [
"Bash(npm run lint)",
"Bash(npm run test)",
"Bash(npm run test *)",
"Bash(npm run build)",
"Bash(git status)",
"Bash(git diff)",
"Bash(git diff *)",
"WebFetch(domain:code.claude.com)"
],
"ask": [
"Bash(npm install *)",
"Bash(pnpm add *)",
"Bash(git push *)",
"Bash(wrangler deploy *)",
"Bash(vercel deploy *)",
"Bash(terraform apply *)",
"Bash(kubectl apply *)"
],
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)",
"Bash(curl *)",
"Bash(wget *)",
"Bash(rm -rf *)"
]
}
}
Lo importante es mantener estrecha la lista segura. Bash(npm *) puede pasar de tests a install o publish. Bash(git *) puede pasar de diff a push. Lectura, lint, test y build pueden ir estrechos en allow; install, push, deploy y apply deben preguntar.
Presupuesto y log en JSON
Los permisos son solo la mitad. La otra mitad es el coste. Investigaciones largas, tests fallidos repetidos, sesiones en segundo plano y logs enormes pueden crecer en silencio. Un presupuesto pequeño y un log diario hacen visible la revisión.
{
"date": "2026-06-03",
"dailyLimitUsd": 6,
"warnAtUsd": 4,
"usageSource": "/usage plus Claude Console",
"safeAllow": [
"Bash(npm run lint)",
"Bash(npm run test)",
"Bash(git diff *)"
],
"askFirst": [
"Bash(npm install *)",
"Bash(git push *)",
"Bash(wrangler deploy *)"
],
"mustDeny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)"
],
"handoffRequired": true
}
{
"date": "2026-06-03",
"spentUsd": 1.85,
"usageChecked": true,
"settingsChecked": true,
"permissionsReviewed": [
"/permissions",
".claude/settings.json"
],
"openAllowances": [
"Bash(npm run lint)",
"Bash(npm run test *)"
],
"handoff": [
"No deploy allowance left open",
"Claude stopped before production data work"
]
}
Guárdalos como .claude/permission-budget.json y .claude/daily-claude-log.json. Un spreadsheet puede servir para reportes, pero JSON es mejor para revisión de PR y automatización.
Script Node para auditar
Guarda esto como scripts/audit-claude-loop.mjs y ejecuta node scripts/audit-claude-loop.mjs. No requiere dependencias externas. Detecta Bash demasiado amplio, comandos de deploy en allow, reglas .env ausentes, exceso de presupuesto y falta de handoff.
#!/usr/bin/env node
import fs from "node:fs";
const readJson = (file) => JSON.parse(fs.readFileSync(file, "utf8"));
const budget = readJson(".claude/permission-budget.json");
const log = readJson(".claude/daily-claude-log.json");
const settings = readJson(".claude/settings.json");
const problems = [];
const permissions = settings.permissions ?? {};
const allow = new Set(permissions.allow ?? []);
const ask = new Set(permissions.ask ?? []);
const deny = new Set(permissions.deny ?? []);
const hasPattern = (items, pattern) => [...items].some((item) => pattern.test(item));
if (typeof budget.dailyLimitUsd !== "number" || budget.dailyLimitUsd <= 0) {
problems.push("dailyLimitUsd must be a positive number");
}
if (typeof budget.warnAtUsd !== "number" || budget.warnAtUsd >= budget.dailyLimitUsd) {
problems.push("warnAtUsd must be lower than dailyLimitUsd");
}
if (log.spentUsd > budget.dailyLimitUsd) {
problems.push(`spentUsd ${log.spentUsd} exceeds daily limit ${budget.dailyLimitUsd}`);
}
if (log.spentUsd >= budget.warnAtUsd) {
console.warn(`WARN: spentUsd ${log.spentUsd} has reached warnAtUsd ${budget.warnAtUsd}`);
}
if (!log.usageChecked) problems.push("Run /usage and mark usageChecked true");
if (!log.settingsChecked) problems.push("Review /permissions and mark settingsChecked true");
if (allow.has("Bash") || allow.has("Bash(*)") || hasPattern(allow, /^Bash\(\*.*\)$/)) {
problems.push("Do not allow every Bash command");
}
if (hasPattern(allow, /(deploy|terraform apply|kubectl apply|git push)/)) {
problems.push("Deploy, infrastructure, and push commands must be ask-first, not allow");
}
for (const rule of budget.askFirst ?? []) {
if (!ask.has(rule)) problems.push(`Missing ask rule: ${rule}`);
}
for (const rule of budget.mustDeny ?? []) {
if (!deny.has(rule)) problems.push(`Missing deny rule: ${rule}`);
}
if (!hasPattern(deny, /Read\(.*\.env/)) {
problems.push("Deny rules should block .env reads");
}
if (!Array.isArray(log.handoff) || log.handoff.length === 0) {
problems.push("Add at least one handoff note");
}
if (problems.length) {
console.error(problems.map((problem) => `- ${problem}`).join("\n"));
process.exit(1);
}
console.log("Claude Code daily permission budget check passed.");
En CI conviene empezar en modo aviso o manual. Una política demasiado dura el primer día suele producir atajos.
Cuatro casos de uso
Primero, artículos y documentación. Markdown, MDX, enlaces internos, CTAs, correcciones y rutas de imágenes rara vez tocan secretos o producción. Permite de forma estrecha lecturas, git diff, lint, tests y build local para que Claude Code avance sin fricción innecesaria.
Segundo, cambios de dependencias. npm install y pnpm add afectan lockfiles, postinstall, licencias, vulnerabilidades y bundle size. Déjalos en ask y pide motivo, alternativa considerada y plan de retirada.
Tercero, deploys y migraciones. wrangler deploy, vercel deploy, terraform apply, kubectl apply y migraciones cambian estado externo. Claude Code puede preparar comando, impacto, rollback, URL de verificación y monitorización, pero la ejecución debe aprobarla una persona.
Cuarto, handoff de equipo. Si otra persona continúa, necesita permisos abiertos, comandos de prueba, acciones bloqueadas y presupuesto restante. Sin esa nota se repite la investigación y se reabren riesgos.
Errores que conviene evitar
El error más común es una regla Bash amplia. Bash(npm *) y Bash(git *) mezclan hábitos de lectura con comandos que cambian estado. Usa comandos exactos para la vía diaria.
Otro error es olvidar una autorización de deploy. En un incidente, wrangler deploy * puede pasar de ask a allow. Si sigue ahí al día siguiente, el trabajo normal tiene poder de producción.
El tercer error es ignorar el crecimiento de tokens y coste. Una investigación larga parece productiva, pero puede quemar presupuesto. Revisa /usage, compara con Console cuando importe la factura y detén sesiones sin valor claro.
Por último, no confundas prompt con enforcement. “No leas secretos” orienta. Read(./.env) en deny bloquea. El harness, la estructura operativa del agente, necesita prompt, settings, logs y revisión juntos.
Productos, formación y rollout
Para practicar solo, copia los JSON y el script en un repositorio pequeño. Para checklists reutilizables, plantillas CLAUDE.md y prompts de revisión, usa /products/. Para un rollout de equipo con permisos, control de coste, CI, formación de reviewers y reglas por repositorio, usa /training/.
Nota práctica (実際に試した結果): la mejora más grande no vino de bloquear todos los comandos peligrosos. Vino de revisar cinco minutos cada mañana /permissions, /usage, git diff y la nota de handoff. Las reglas allow estrechas siguieron siendo útiles, mientras deploys, billing y secrets volvían siempre a ask o deny.
PDF gratis: cheatsheet de Claude Code
Introduce tu email y descarga una hoja con comandos, hábitos de revisión y flujos seguros.
Cuidamos tus datos y no enviamos spam.
Sobre el autor
Masa
Ingeniero enfocado en workflows prácticos con Claude Code.
Artículos relacionados
Escalera de permisos de Claude Code para ampliar acceso sin perder control
Pasa de read-only a ediciones limitadas, comandos de prueba y checks de deploy con menos riesgo.
Claude Code Small PR Proof Pack: cambios pequeños que sí se pueden revisar
Un paquete de prueba para PRs de Claude Code: diff, checks, URL pública, CTA y rollback.
Gate de revisión antes del commit con Claude Code
Cómo revisar con Claude Code antes del commit: diff, build, URL pública, Gumroad, consultoría, tests y archivos ajenos.