TL;DR El effort se define en cinco sitios y solo gana uno. La variable de entorno pisa hasta el flag con el que arrancaste, y lo hace en silencio. Un
effortLevel: "max"en tusettings.jsonse ignora entero. La única forma de ver el nivel real desde un script es una skill de seis líneas con${CLAUDE_EFFORT}. La cadena de precedencia está comprobada a mano, fila por fila, en la v2.1.220.
Abre tu ~/.claude/settings.json. Si pone "effortLevel": "high", das por hecho que trabajas en high.
Puede que sí. Hay cuatro formas de que no, y ninguna de las cuatro te avisa.
Esto es lo que pasa cuando arrancas pidiendo el nivel más alto que existe:
$ CLAUDE_CODE_EFFORT_LEVEL=low claude --effort max -p "/whicheffort"
EFFORT=low
Pediste max de forma explícita, en el propio comando. Corres en low. Ni un aviso, ni una línea en la cabecera, nada. (/whicheffort es una skill de seis líneas que montamos al final del tip; es la única manera de sacar ese dato.)
Los cinco sitios, y quién gana a quién
El effort no vive en un lugar. Vive en cinco, con este orden:
| Dónde se define | Alcance | Gana a |
|---|---|---|
CLAUDE_CODE_EFFORT_LEVEL |
La sesión | Absolutamente todo |
effort: en el frontmatter de una skill o subagente |
Mientras esa skill corre | Todo menos la variable |
/effort y el flag --effort |
La sesión (persiste, salvo max) |
Settings y default |
effortLevel en settings.json |
Permanente | Solo al default |
| El default del modelo | Cuando no hay nada más |
Probé las cinco filas una a una. Dos resultados que no son obvios: el effort: del frontmatter de una skill pisa el flag con el que lanzaste la sesión (--effort max con una skill en low corre en low), pero la variable de entorno lo pisa a él. Es el mismo lío que tienes con el modelo, con reglas propias: si el tuyo se te queda pegado o se te resetea, eso va aparte.
Los cuatro fallos silenciosos
1. El typo en el settings.
$ claude --settings '{"effortLevel":"hight"}' -p "/whicheffort"
EFFORT=high
Escribí hight. Ni un aviso. Corre al default y sigue su vida. Compara con el flag, que sí valida lo que le pasas:
$ claude --effort bananas -p "hola"
Warning: Unknown --effort value 'bananas' — ignoring it and using the default effort.
Valid values: low, medium, high, xhigh, max.
El flag te grita. El fichero se lo traga.
2. max en el settings no existe.
$ claude --settings '{"effortLevel":"max"}' -p "/whicheffort"
EFFORT=high
effortLevel solo acepta low, medium, high y xhigh. max y ultracode son de sesión y ahí no se aceptan. Si lo escribiste hace meses convencido de que ibas a tope, llevas meses en high.
3. La variable de entorno lo pisa todo, incluido tu flag. Es el ejemplo de arriba. Si la exportaste una vez en el .zshrc para un experimento, sigue mandando hoy en cada sesión.
4. max no sobrevive a cerrar la terminal. low, medium, high y xhigh persisten cuando los pones en una sesión interactiva. max es la excepción: dura esa sesión y se va, salvo que lo fijes por variable de entorno.
Hay un quinto caso, y ese sí lo tienes contado: estrenar Fable 5, Opus 4.8 o Opus 4.7 te impone el default de ese modelo aunque tuvieras otro puesto.
De todas las superficies, solo una te dice la verdad cuando algo la está pisando:
$ CLAUDE_CODE_EFFORT_LEVEL=low claude -p "/effort max"
Not applied: CLAUDE_CODE_EFFORT_LEVEL=low overrides effort this session, and max is session-only (nothing saved)
Dos avisos en una línea, y es la única que los da.
Cómo ver el nivel real, también dentro de un script
En interactivo tienes el nivel en la cabecera de sesión, junto al modelo. Fuera de ahí te quedas a ciegas: /status no está disponible en headless, y --output-format json te devuelve el modelo, el coste al céntimo, los tokens de caché y hasta si el modo rápido está apagado, pero del effort no dice nada.
La salida está en una variable de contexto que casi nadie usa. Crea .claude/skills/whicheffort/SKILL.md:
---
name: whicheffort
description: Reporta el nivel de effort activo
---
Responde exactamente con esta línea y nada más:
EFFORT=${CLAUDE_EFFORT}
Y ya tienes un chivato que funciona igual en tu terminal que en CI:
$ claude -p "/whicheffort"
EFFORT=high
$ claude --effort ultracode -p "/whicheffort"
EFFORT=xhigh
Ese segundo resultado tiene su gracia: ultracode no es un nivel de effort, así que reporta como xhigh, que es lo que se envía de verdad a la API.
Qué te juegas cuando el nivel no es el que crees
Si esto solo cambiara cuánto piensa Claude, sería un problema de factura. Es más que eso. Según la documentación de Anthropic, bajar el effort reduce todos los tokens de la respuesta, y eso incluye las llamadas a herramientas: Claude agrupa operaciones, hace menos llamadas, va directo a la acción sin explicar el plan y confirma con dos palabras.
Traducido a lo que te importa: con el effort por debajo de lo que creías, Claude toca tu código habiendo leído menos ficheros tuyos. No es que piense peor, es que mira menos antes de escribir.
Por eso conviene comprobarlo una vez y olvidarse, en vez de ir tocándolo. Cuál elegir en cada caso ya está resuelto en cómo ajustar el effort level y en mi ruta entre Sonnet 5, Opus 5 y Fable 5. Y si llegaste aquí buscando gastar menos, el effort es un frente de diez: los otros nueve, aquí.
Referencia
| Superficie | ¿Persiste? | ¿Avisa si falla? | Acepta max |
|---|---|---|---|
CLAUDE_CODE_EFFORT_LEVEL |
Mientras esté exportada | No | Sí |
effort: en frontmatter |
Solo mientras corre la skill | No | Sí |
/effort |
Sí, salvo max |
Sí, es la única | Sí, esa sesión |
--effort al lanzar |
Solo esa sesión | Sí, si el valor no existe | Sí |
effortLevel en settings |
Sí | No | No, lo ignora |
Si todo esto te suena a que el effort es un mando importante, es porque es el único que sigue conectado a algo.
Documentación oficial: Model configuration: adjust effort level · Effort · Skills: frontmatter
Requisitos
- Comprobado en Claude Code v2.1.220. La variable
${CLAUDE_EFFORT}está disponible dentro de skills.