{"libro":"Claude Code en producción","edicion":"v2026.08","cli":"2.1.258 (Claude Code)","sistema":"Linux 6.17.0-1022-azure","fecha":"2026-09-02 06:49:34 UTC","resumen":{"PASA":95,"FALLA":0,"REVISAR":43,"OMITIDO":6},"pruebas":[{"id":"CLI-001","cap":"02 · Instalación y autenticación","dice":"El binario responde a --version y devuelve una versión semántica.","como":"claude --version","veredicto":"PASA","porque":"comprobado","salida":"2.1.258 (Claude Code)"},{"id":"CLI-002","cap":"02 · Instalación y autenticación","dice":"claude doctor comprueba la salud de la instalación sin abrir sesión.","como":"claude doctor","veredicto":"PASA","porque":"comprobado","salida":"Claude Code doctor Running: native (2.1.258) Commit: b3cd543a1f6f Platform: linux-x64 Path: /home/runner/.local/share/claude/versions/2.1.258 Config install met"},{"id":"CLI-003","cap":"02 · Instalación y autenticación","dice":"claude setup-token crea un token de larga duración para suscriptores.","como":"claude --help","veredicto":"PASA","porque":"comprobado","salida":"Usage: claude [options] [command] [prompt] Claude Code - starts an interactive session by default, use -p/--print for non-interactive output Arguments: prompt Y"},{"id":"CLI-004","cap":"02 · Instalación, autenticación y versiones","dice":"claude doctor informa del método de instalación, del canal de actualización y del resultado del último intento.","como":"claude doctor","veredicto":"PASA","porque":"comprobado","salida":"Claude Code doctor Running: native (2.1.258) Commit: b3cd543a1f6f Platform: linux-x64 Path: /home/runner/.local/share/claude/versions/2.1.258 Config install met"},{"id":"CLI-005","cap":"02 · Instalación, autenticación y versiones","dice":"claude install acepta como destino un canal o un número de versión concreto, que es la vía para reproducir un fallo ajeno.","como":"claude install --help","veredicto":"PASA","porque":"comprobado","salida":"Usage: claude install [options] [target] Install Claude Code native build. Use [target] to specify version (stable, latest, or specific version) Options: --forc"},{"id":"CLI-006","cap":"02 · Instalación, autenticación y versiones","dice":"claude auth gestiona el inicio de sesión y expone status, que dice qué credencial está activa sin abrir sesión.","como":"claude auth --help","veredicto":"PASA","porque":"comprobado","salida":"Usage: claude auth [options] [command] Manage authentication Options: -h, --help Display help for command Commands: help [command] display help for command logi"},{"id":"CLI-007","cap":"02 · Instalación, autenticación y versiones","dice":"La instalación nativa deja en el PATH un enlace simbólico a una versión concreta bajo ~/.local/share/claude/versions/, y por eso conviven varias versiones en disco.","como":"bash -c L=\"$HOME/.local/bin/claude\"; if [ -L \"$L\" ]; then readlink \"$L\" | grep -q \"/.local/share/claude/versions/\" && echo LANZADOR-ES-ENLACE-A-VERSIONS || echo ENLACE-A-OTRO-SITIO; else echo NO-APLIC","veredicto":"PASA","porque":"comprobado","salida":"LANZADOR-ES-ENLACE-A-VERSIONS"},{"id":"CLI-008","cap":"02 · Instalación, autenticación y versiones","dice":"claude doctor termina con código 0 aunque reporte ajustes inválidos: el código de salida no sirve como comprobación de configuración.","como":"bash -c d=$(mktemp -d); mkdir -p \"$d/.claude\"; echo \"{\" > \"$d/.claude/settings.json\"; cd \"$d\"; claude doctor > out.txt 2>&1; c=$?; grep -q \"Invalid settings\" out.txt && [ \"$c\" = \"0\" ] && echo AJUSTES-","veredicto":"PASA","porque":"comprobado","salida":"AJUSTES-INVALIDOS-Y-SALIDA-CERO"},{"id":"CLI-009","cap":"02 · Instalación, autenticación y versiones","dice":"La documentación oficial condiciona comportamiento a versiones concretas del CLI: authentication.md cita seis o más.","como":"bash -c D=$(curl -fsS --max-time 25 https://code.claude.com/docs/en/authentication.md 2>/dev/null) || { echo SIN-RED; exit 0; }; printf \"%s\" \"$D\" | grep -oE \"v2[.]1[.][0-9]+\" | sort -u | wc -l","veredicto":"PASA","porque":"comprobado","salida":"8"},{"id":"CLI-010","cap":"02 · Instalación, autenticación y versiones","dice":"El binario acepta un tercer canal de publicación, rc, que la documentación oficial no menciona.","como":"","veredicto":"REVISAR","porque":"Comprobado el 17-ago-2026 con la 2.1.233. Poniendo un autoUpdatesChannel inválido en un settings.json, claude doctor con","salida":""},{"id":"AUT-001","cap":"02 · Instalación, autenticación y versiones","dice":"El orden de precedencia de credenciales tiene siete niveles, y una sesión de pasarela de aplicaciones de Claude queda por encima de los siete.","como":"","veredicto":"REVISAR","porque":"Eran cuatro en el material propio del 12-ago-2026 y son siete en authentication.md del 17-ago-2026. Es una lista que cre","salida":""},{"id":"ENT-001","cap":"02 · Instalación, autenticación y versiones","dice":"El laboratorio del módulo 02 deja los mandos del proyecto versionados en gestor-pedidos.","como":"test -f .claude/settings.json","veredicto":"PASA","porque":"comprobado","salida":""},{"id":"ENT-002","cap":"02 · Instalación, autenticación y versiones","dice":"El laboratorio del módulo 02 deja la versión del CLI anotada en el repositorio, no en la cabeza de nadie.","como":"grep -qE [0-9]+[.][0-9]+[.][0-9]+ ENTORNO.md","veredicto":"PASA","porque":"comprobado","salida":""},{"id":"CTX-001","cap":"03 · Memoria y contexto","dice":"--add-dir da acceso a directorios fuera del proyecto actual.","como":"claude --help","veredicto":"PASA","porque":"comprobado","salida":"Usage: claude [options] [command] [prompt] Claude Code - starts an interactive session by default, use -p/--print for non-interactive output Arguments: prompt Y"},{"id":"CTX-002","cap":"03 · Memoria y contexto","dice":"--autocompact controla el tamaño de la ventana antes de compactar.","como":"claude --help","veredicto":"PASA","porque":"comprobado","salida":"Usage: claude [options] [command] [prompt] Claude Code - starts an interactive session by default, use -p/--print for non-interactive output Arguments: prompt Y"},{"id":"CTX-003","cap":"03 · Memoria y contexto","dice":"--bare arranca sin CLAUDE.md, sin hooks y sin plugins, para depurar contexto.","como":"claude --help","veredicto":"PASA","porque":"comprobado","salida":"Usage: claude [options] [command] [prompt] Claude Code - starts an interactive session by default, use -p/--print for non-interactive output Arguments: prompt Y"},{"id":"CTX-004","cap":"03 · Memoria y contexto","dice":"El modo mínimo se salta el descubrimiento automático de CLAUDE.md, además de hooks, plugins y auto memory.","como":"claude --help","veredicto":"PASA","porque":"comprobado","salida":"Usage: claude [options] [command] [prompt] Claude Code - starts an interactive session by default, use -p/--print for non-interactive output Arguments: prompt Y"},{"id":"CTX-005","cap":"03 · Memoria y contexto","dice":"El laboratorio del módulo 03 deja un CLAUDE.md en la raíz de gestor-pedidos.","como":"test -f CLAUDE.md","veredicto":"PASA","porque":"comprobado","salida":""},{"id":"CTX-006","cap":"03 · Memoria y contexto","dice":"El CLAUDE.md del laboratorio declara por escrito cuál es el archivo de configuración que manda, y lo que declara es verdad.","como":"grep -q Manda `config.py`, y `app.py` lo importa de verdad CLAUDE.md","veredicto":"PASA","porque":"comprobado","salida":""},{"id":"CTX-007","cap":"03 · Memoria y contexto","dice":"En gestor-pedidos utils.py sigue muerto: nadie lo importa, calcular_iva() incluida.","como":"bash -c n=$(grep -rlE \"^(import|from) utils\" --include=*.py . | wc -l); [ \"$n\" = \"0\" ] && echo NADIE-IMPORTA-UTILS || echo ALGUIEN-LO-IMPORTA","veredicto":"PASA","porque":"comprobado","salida":"NADIE-IMPORTA-UTILS"},{"id":"CTX-008","cap":"03 · Memoria y contexto","dice":"El endpoint muerto de gestor-pedidos sigue expuesto y lee la tabla pedidos_2019.","como":"grep -q pedidos_2019 app.py","veredicto":"PASA","porque":"comprobado","salida":""},{"id":"CTX-009","cap":"03 · Memoria y contexto","dice":"El binario conoce las tres claves de memoria del módulo: claudeMdExcludes, autoMemoryEnabled y autoMemoryDirectory.","como":"bash -c d=$(mktemp -d); mkdir -p \"$d/.claude\"; printf \"%b\" \"{\\0042claudeMdExcludes\\0042: 5, \\0042autoMemoryEnabled\\0042: 7, \\0042autoMemoryDirectory\\0042: 9}\" > \"$d/.claude/settings.json\"; cd \"$d\"; cl","veredicto":"PASA","porque":"comprobado","salida":"CLAVES-RECONOCIDAS=3"},{"id":"CTX-010","cap":"03 · Memoria y contexto","dice":"La documentación oficial fija el objetivo de tamaño de un CLAUDE.md por debajo de 200 líneas.","como":"bash -c D=$(curl -fsS --max-time 25 https://code.claude.com/docs/en/memory.md 2>/dev/null) || { echo SIN-RED; exit 0; }; printf \"%s\" \"$D\" | grep -c \"under 200 lines\"","veredicto":"PASA","porque":"comprobado","salida":"1"},{"id":"CTX-011","cap":"03 · Memoria y contexto","dice":"El CLAUDE.md de la raíz y las reglas sin paths se releen del disco y se reinyectan tras compactar; las reglas con paths y los CLAUDE.md anidados no.","como":"bash -c D=$(curl -fsS --max-time 25 https://code.claude.com/docs/en/context-window.md 2>/dev/null) || { echo SIN-RED; exit 0; }; printf \"%s\" \"$D\" | grep -c \"Re-injected from disk\"","veredicto":"PASA","porque":"comprobado","salida":"3"},{"id":"CTX-012","cap":"03 · Memoria y contexto","dice":"Un CLAUDE.md de 66 líneas costó 1.178 tokens de entrada por turno, y 40 líneas de notas dentro de un comentario HTML costaron exactamente cero.","como":"","veredicto":"REVISAR","porque":"Medido el 18-ago-2026 con la 2.1.234 sobre D6-repo-feo/gestor-pedidos. Método: misma petición trivial, mismo repo, varia","salida":""},{"id":"CTX-013","cap":"03 · Memoria y contexto","dice":"Con inicio de sesión de suscripción, el modo mínimo no autentica, y el mensaje de error habla de red en vez de credenciales.","como":"","veredicto":"REVISAR","porque":"Comprobado el 18-ago-2026 con la 2.1.234. claude -p con --bare devuelve: Authentication error, This may be a temporary n","salida":""},{"id":"PRM-001","cap":"04 · Permisos y sandbox","dice":"--allowedTools acepta una lista de herramientas permitidas.","como":"claude --help","veredicto":"PASA","porque":"comprobado","salida":"Usage: claude [options] [command] [prompt] Claude Code - starts an interactive session by default, use -p/--print for non-interactive output Arguments: prompt Y"},{"id":"PRM-002","cap":"04 · Permisos y sandbox","dice":"Existe una bandera para saltarse todos los permisos, y la documentación la marca como peligrosa.","como":"claude --help","veredicto":"PASA","porque":"comprobado","salida":"Usage: claude [options] [command] [prompt] Claude Code - starts an interactive session by default, use -p/--print for non-interactive output Arguments: prompt Y"},{"id":"PRM-003","cap":"04 · Permisos y sandbox","dice":"El archivo de ajustes de usuario vive en ~/.claude/settings.json.","como":"test -e /home/runner/.claude","veredicto":"PASA","porque":"existe","salida":""},{"id":"PRM-004","cap":"04 · Permisos y sandbox","dice":"La bandera --permission-mode enumera los seis modos, y al modo por defecto lo llama manual.","como":"claude --permission-mode zzz -p x","veredicto":"PASA","porque":"comprobado","salida":"error: option '--permission-mode <mode>' argument 'zzz' is invalid. Allowed choices are acceptEdits, auto, bypassPermissions, manual, dontAsk, plan."},{"id":"PRM-005","cap":"04 · Permisos y sandbox","dice":"El esquema de configuración enumera los mismos seis modos, y ahí el nombre del primero es default y no manual.","como":"claude doctor","veredicto":"PASA","porque":"comprobado","salida":"Claude Code doctor Running: native (2.1.258) Commit: b3cd543a1f6f Platform: linux-x64 Path: /home/runner/.local/share/claude/versions/2.1.258 Config install met"},{"id":"PRM-006","cap":"04 · Permisos y sandbox","dice":"Las claves sandbox.enabled, sandbox.network.strictAllowlist y autoMode.environment existen en el esquema del binario instalado, no solo en la documentación.","como":"claude doctor","veredicto":"PASA","porque":"comprobado","salida":"Claude Code doctor Running: native (2.1.258) Commit: b3cd543a1f6f Platform: linux-x64 Path: /home/runner/.local/share/claude/versions/2.1.258 Config install met"},{"id":"PRM-007","cap":"04 · Permisos y sandbox","dice":"claude auto-mode defaults imprime las cuatro listas del clasificador en JSON.","como":"claude auto-mode defaults","veredicto":"PASA","porque":"comprobado","salida":"{ \"allow\": [ \"Security Discussion: Reading, discussing, reviewing, or writing security-related code, docs, configs, or threat models as part of the user's task "},{"id":"PRM-008","cap":"04 · Permisos y sandbox","dice":"Las reglas permissions.allow del settings.json de un proyecto se ignoran hasta aceptar el diálogo de confianza, y el aviso sale por la salida de error.","como":"","veredicto":"REVISAR","porque":"Medido el 19-ago-2026 con la 2.1.235 sobre D6-repo-feo/gestor-pedidos. Misma petición (crear un archivo vacío con touch)","salida":""},{"id":"PRM-009","cap":"04 · Permisos y sandbox","dice":"Una regla deny de Read bloquea cat y no bloquea a un subproceso que abra el mismo archivo por su cuenta.","como":"","veredicto":"REVISAR","porque":"Medido el 19-ago-2026 con la 2.1.235 sobre D6-repo-feo/gestor-pedidos, con deny Read(./secretos/**) puesto en el setting","salida":""},{"id":"PRM-010","cap":"04 · Permisos y sandbox","dice":"Las reglas de permisos no ocupan contexto: 24 reglas deny cuestan exactamente los mismos tokens de entrada que ninguna.","como":"","veredicto":"REVISAR","porque":"Medido el 19-ago-2026 con la 2.1.235 sobre D6-repo-feo/gestor-pedidos. Misma petición trivial (Responde solo con la pala","salida":""},{"id":"PRM-011","cap":"04 · Permisos y sandbox","dice":"--dangerously-skip-permissions se niega a arrancar como root o con sudo en Linux y macOS.","como":"","veredicto":"REVISAR","porque":"Comprobado el 19-ago-2026 con la 2.1.235 en el sandbox de la nube, que corre como root: el mensaje es --dangerously-skip","salida":""},{"id":"PRM-012","cap":"04 · Permisos y sandbox","dice":"Los tres perfiles de permisos del módulo están escritos y disponibles.","como":"test -e /home/runner/work/manual-claude-code/manual-claude-code/entregables/plantillas/permisos","veredicto":"PASA","porque":"existe","salida":""},{"id":"PRM-013","cap":"04 · Permisos y sandbox","dice":"La plantilla de contenedor de desarrollo con red restringida está escrita.","como":"test -e /home/runner/work/manual-claude-code/manual-claude-code/entregables/plantillas/devcontainer/devcontainer.json","veredicto":"PASA","porque":"existe","salida":""},{"id":"PRM-014","cap":"04 · Permisos y sandbox","dice":"El laboratorio deja versionada la regla que prohíbe leer el directorio de secretos.","como":"python3 -c import json,sys; d=json.load(open(\".claude/settings.json\")); sys.exit(0 if \"Read(./secretos/**)\" in d[\"permissions\"][\"deny\"] else 1)","veredicto":"PASA","porque":"comprobado","salida":""},{"id":"PRM-015","cap":"04 · Permisos y sandbox","dice":"El laboratorio explica por escrito por qué está cada regla y qué es lo que las reglas no protegen.","como":"test -e /home/runner/work/manual-claude-code/manual-claude-code/D6-repo-feo/gestor-pedidos/PERMISOS.md","veredicto":"PASA","porque":"existe","salida":""},{"id":"PRM-016","cap":"04 · Permisos y sandbox","dice":"El archivo de secretos del laboratorio está excluido del control de versiones.","como":"git check-ignore D6-repo-feo/gestor-pedidos/secretos/pasarela.env","veredicto":"PASA","porque":"comprobado","salida":"D6-repo-feo/gestor-pedidos/secretos/pasarela.env"},{"id":"HOK-001","cap":"05 · Hooks","dice":"Los hooks se configuran en settings.json, no en un archivo aparte.","como":"","veredicto":"REVISAR","porque":"Comprobar contra la documentación oficial en cada revisión trimestral.","salida":""},{"id":"HOK-002","cap":"05 · Hooks","dice":"Un hook PreToolUse puede denegar una llamada devolviendo permissionDecision deny en la salida estándar, sin usar el código de salida 2.","como":"bash -c hooks/veto-secretos.sh < hooks/ejemplos/lectura-de-secreto.json","veredicto":"PASA","porque":"comprobado","salida":"{ \"hookSpecificOutput\": { \"hookEventName\": \"PreToolUse\", \"permissionDecision\": \"deny\", \"permissionDecisionReason\": \"Bloqueado por el hook veto-secretos: /repo/s"},{"id":"HOK-003","cap":"05 · Hooks","dice":"Un hook que no tiene nada que vetar sale con código 0 y sin imprimir nada, y así deja seguir el flujo normal de permisos.","como":"bash -c hooks/veto-secretos.sh < hooks/ejemplos/lectura-inocua.json | grep -q deny","veredicto":"PASA","porque":"comprobado","salida":""},{"id":"HOK-004","cap":"05 · Hooks","dice":"Un hook puede decidir mirando el CONTENIDO que se va a escribir, cosa que una regla de permisos no puede hacer porque protege rutas.","como":"bash -c hooks/veto-credenciales.sh < hooks/ejemplos/escritura-con-credencial.json","veredicto":"PASA","porque":"comprobado","salida":"{ \"hookSpecificOutput\": { \"hookEventName\": \"PreToolUse\", \"permissionDecision\": \"deny\", \"permissionDecisionReason\": \"Bloqueado por el hook veto-credenciales: lo "},{"id":"HOK-005","cap":"05 · Hooks","dice":"El veto por contenido deja pasar un archivo sin credenciales dentro.","como":"bash -c hooks/veto-credenciales.sh < hooks/ejemplos/escritura-inocua.json | grep -q deny","veredicto":"PASA","porque":"comprobado","salida":""},{"id":"HOK-006","cap":"05 · Hooks","dice":"El hook de formateo sale con código 0 cuando lo editado no es Python, en vez de romper el turno.","como":"bash -c hooks/formatear.sh < hooks/ejemplos/edicion-no-python.json","veredicto":"PASA","porque":"comprobado","salida":""},{"id":"HOK-007","cap":"05 · Hooks","dice":"Los hooks del laboratorio viven fuera de .claude/, en hooks/, y son ejecutables.","como":"bash -c test -x hooks/veto-secretos.sh -a -x hooks/veto-credenciales.sh -a -x hooks/formatear.sh -a ! -d .claude/hooks","veredicto":"PASA","porque":"comprobado","salida":""},{"id":"HOK-008","cap":"05 · Hooks","dice":"El settings.json del laboratorio declara sus hooks con el marcador de ruta CLAUDE_PROJECT_DIR y en forma de ejecutable, con args.","como":"bash -c grep -q hooks/veto-secretos.sh .claude/settings.json && grep -q CLAUDE_PROJECT_DIR .claude/settings.json && grep -q args .claude/settings.json","veredicto":"PASA","porque":"comprobado","salida":""},{"id":"HOK-009","cap":"05 · Hooks","dice":"El esquema del binario instalado conoce el bloque hooks con sus eventos y la clave disableAllHooks, no solo la documentación.","como":"claude doctor","veredicto":"PASA","porque":"comprobado","salida":"Claude Code doctor Running: native (2.1.258) Commit: b3cd543a1f6f Platform: linux-x64 Path: /home/runner/.local/share/claude/versions/2.1.258 Config install met"},{"id":"HOK-010","cap":"05 · Hooks","dice":"El CLI documenta --debug-file, que es la vía para leer qué hooks casaron y con qué código de salida.","como":"claude --help","veredicto":"PASA","porque":"comprobado","salida":"Usage: claude [options] [command] [prompt] Claude Code - starts an interactive session by default, use -p/--print for non-interactive output Arguments: prompt Y"},{"id":"HOK-011","cap":"05 · Hooks","dice":"Una instrucción del CLAUDE.md se incumple de forma impredecible y un hook de tipo command no: 5 de 7 frente a 0 de 7.","como":"","veredicto":"REVISAR","porque":"Medido el 24-ago-2026 con la 2.1.241 sobre D6-repo-feo/gestor-pedidos. Petición idéntica en las catorce ejecuciones: cre","salida":""},{"id":"HOK-012","cap":"05 · Hooks","dice":"En un espacio de trabajo sin confianza aceptada, claude -p ignora las reglas allow del settings.json del proyecto y en cambio SÍ ejecuta los hooks declarados en ese mismo archivo.","como":"","veredicto":"REVISAR","porque":"Medido el 24-ago-2026 con la 2.1.241, dos repeticiones. Un solo .claude/settings.json con permissions.allow Bash(touch *","salida":""},{"id":"HOK-013","cap":"05 · Hooks","dice":"Un archivo metido en el prompt con @ no dispara ningún hook PreToolUse, porque no hay llamada a herramienta; una regla deny de permisos sí lo cubre.","como":"","veredicto":"REVISAR","porque":"Medido el 24-ago-2026 con la 2.1.241, dos repeticiones por caso, sobre el laboratorio con veto-secretos.sh puesto y func","salida":""},{"id":"HOK-014","cap":"05 · Hooks","dice":"Declarar hooks no cuesta contexto: seis hooks configurados dan el mismo recuento de tokens, al token, que ninguno.","como":"","veredicto":"REVISAR","porque":"Medido el 24-ago-2026 con la 2.1.241. Misma petición trivial (responder solo con la palabra OK), mismo repositorio, dos ","salida":""},{"id":"HOK-015","cap":"05 · Hooks","dice":"Un hook PostToolUse que reformatea el archivo recién editado consigue el formateo en las tres repeticiones, y a cambio triplica las llamadas a herramienta del turno.","como":"","veredicto":"REVISAR","porque":"Medido el 24-ago-2026 con la 2.1.241 sobre D6-repo-feo/gestor-pedidos, tres repeticiones por fila. Misma petición: cambi","salida":""},{"id":"MCP-001","cap":"06 · MCP","dice":"claude mcp gestiona los servidores MCP desde la línea de comandos.","como":"claude mcp --help","veredicto":"PASA","porque":"comprobado","salida":"Usage: claude mcp [options] [command] Configure and manage MCP servers Options: -h, --help Display help for command Commands: add [options] <name> <commandOrUrl"},{"id":"MCP-002","cap":"06 · MCP","dice":"Por defecto solo se cargan los nombres de las herramientas MCP; los esquemas van diferidos y se traen bajo demanda con tool search.","como":"","veredicto":"REVISAR","porque":"Corrige material propio erróneo (12-ago-2026). Depende de ENABLE_TOOL_SEARCH: auto carga esquemas si caben en el 10 por ","salida":""},{"id":"MCP-003","cap":"06 · MCP","dice":"Un servidor MCP por stdio se puede escribir sin dependencias: basta con responder initialize y tools/list en JSON-RPC por la entrada y salida estándar.","como":"bash -c CLAUDE_PROJECT_DIR=$PWD python3 mcp/servidor-pedidos.py < mcp/ejemplos/handshake.jsonl","veredicto":"PASA","porque":"comprobado","salida":"{\"jsonrpc\": \"2.0\", \"id\": 1, \"result\": {\"protocolVersion\": \"2024-11-05\", \"capabilities\": {\"tools\": {}}, \"serverInfo\": {\"name\": \"pedidos-solo-lectura\", \"version\":"},{"id":"MCP-004","cap":"06 · MCP","dice":"El servidor de solo lectura rechaza una escritura directa antes de tocar la base de datos.","como":"bash -c CLAUDE_PROJECT_DIR=$PWD python3 mcp/servidor-pedidos.py < mcp/ejemplos/escritura-directa.jsonl","veredicto":"PASA","porque":"comprobado","salida":"{\"jsonrpc\": \"2.0\", \"id\": 1, \"result\": {\"content\": [{\"type\": \"text\", \"text\": \"Solo se admiten consultas SELECT o WITH. Esta base de datos es de solo lectura.\"}],"},{"id":"MCP-005","cap":"06 · MCP","dice":"El servidor rechaza una segunda sentencia escondida detrás de un punto y coma, que es la vía clásica para colar un UPDATE tras un SELECT.","como":"bash -c CLAUDE_PROJECT_DIR=$PWD python3 mcp/servidor-pedidos.py < mcp/ejemplos/segunda-sentencia.jsonl","veredicto":"PASA","porque":"comprobado","salida":"{\"jsonrpc\": \"2.0\", \"id\": 1, \"result\": {\"content\": [{\"type\": \"text\", \"text\": \"Solo se admite una sentencia. Quita el punto y coma.\"}], \"isError\": true}}"},{"id":"MCP-006","cap":"06 · MCP","dice":"El servidor responde a una consulta legítima con los datos de la base, y el laboratorio se reconstruye desde el esquema versionado.","como":"bash -c export CLAUDE_PROJECT_DIR=$PWD && python3 mcp/preparar-bd.py > /dev/null && python3 mcp/servidor-pedidos.py < mcp/ejemplos/consulta-valida.jsonl","veredicto":"PASA","porque":"comprobado","salida":"{\"jsonrpc\": \"2.0\", \"id\": 1, \"result\": {\"content\": [{\"type\": \"text\", \"text\": \"pedidos_totales\\n14\"}]}}"},{"id":"MCP-007","cap":"06 · MCP","dice":"El servidor valida el nombre de tabla en vez de interpolarlo, y responde con error a una tabla que no existe.","como":"bash -c export CLAUDE_PROJECT_DIR=$PWD && python3 mcp/preparar-bd.py > /dev/null && python3 mcp/servidor-pedidos.py < mcp/ejemplos/tabla-inventada.jsonl","veredicto":"PASA","porque":"comprobado","salida":"{\"jsonrpc\": \"2.0\", \"id\": 1, \"result\": {\"content\": [{\"type\": \"text\", \"text\": \"No existe la tabla no_existe.\"}], \"isError\": true}}"},{"id":"MCP-008","cap":"06 · MCP","dice":"El .mcp.json del laboratorio usa el marcador de ruta con valor por defecto y no contiene ningún secreto.","como":"bash -c grep -q CLAUDE_PROJECT_DIR:- .mcp.json && ! grep -qi -e token -e api_key -e secret -e password -e PSP-LIVE .mcp.json","veredicto":"PASA","porque":"comprobado","salida":""},{"id":"MCP-009","cap":"06 · MCP","dice":"Con tool search activada, que es el defecto, un servidor MCP de tres herramientas cuesta 218 tokens de entrada por turno, y cada herramienta añadida cuesta unos 13.","como":"","veredicto":"REVISAR","porque":"Medido el 24-ago-2026 con la 2.1.241 sobre D6-repo-feo/gestor-pedidos. Misma petición trivial (responder solo con la pal","salida":""},{"id":"MCP-010","cap":"06 · MCP","dice":"Apagar tool search cuesta 17.221 tokens de entrada por turno aunque no haya ningún servidor MCP conectado, porque los esquemas de las herramientas propias del CLI también estaban diferidos.","como":"","veredicto":"REVISAR","porque":"Medido el 24-ago-2026 con la 2.1.241, dos repeticiones idénticas. Misma petición trivial, mismo repositorio, sin ningún ","salida":""},{"id":"MCP-011","cap":"06 · MCP","dice":"Un servidor declarado en el .mcp.json de un repositorio conecta en claude -p y sus herramientas están disponibles, aunque claude mcp list lo dé como pendiente de aprobación.","como":"","veredicto":"REVISAR","porque":"Medido el 24-ago-2026 con la 2.1.241, dos repeticiones. Con el .mcp.json del laboratorio sin aprobar nunca, claude mcp l","salida":""},{"id":"SEG-002","cap":"10 · Seguridad y costes","dice":"Que el agente nombre una inyección escondida en un archivo del repositorio depende de la versión y no se puede sostener como afirmación del libro.","como":"","veredicto":"REVISAR","porque":"CADUCÓ Y SE REESCRIBIÓ EL 31-AGO-2026. La entrada anterior afirmaba que la inyección se detecta y se reporta, con el pat","salida":""},{"id":"SKL-001","cap":"07 · Skills y plugins","dice":"claude plugin gestiona los plugins instalados.","como":"claude plugin --help","veredicto":"PASA","porque":"comprobado","salida":"Usage: claude plugin|plugins [options] [command] Manage Claude Code plugins Options: -h, --help Display help for command Commands: details [options] <name> Show"},{"id":"SKL-002","cap":"07 · Skills y plugins","dice":"Las skills se resuelven por /nombre-de-skill incluso en modo mínimo.","como":"claude --help","veredicto":"PASA","porque":"comprobado","salida":"Usage: claude [options] [command] [prompt] Claude Code - starts an interactive session by default, use -p/--print for non-interactive output Arguments: prompt Y"},{"id":"SKL-003","cap":"07 · Skills y plugins","dice":"La skill del laboratorio existe y su disparo está escrito con los síntomas, no con el nombre de la tarea.","como":"python3 -c import sys; t=open('.claude/skills/auditar-endpoint/SKILL.md').read(); sys.exit(0 if 'when_to_use' in t and 'apóstrofe' in t else 1)","veredicto":"PASA","porque":"comprobado","salida":""},{"id":"SKL-004","cap":"07 · Skills y plugins","dice":"El laboratorio explica por escrito qué mira la skill, qué cuesta y qué no garantiza.","como":"test -e /home/runner/work/manual-claude-code/manual-claude-code/D6-repo-feo/gestor-pedidos/SKILLS.md","veredicto":"PASA","porque":"existe","salida":""},{"id":"SKL-005","cap":"07 · Skills y plugins","dice":"Con una petición que nombra la tarea, la skill se dispara aunque su descripción sea de catálogo; con una que cuenta un síntoma no se dispara, y solo lo hace si el síntoma está escrito en when_to_use.","como":"","veredicto":"REVISAR","porque":"Medido el 25-ago-2026 con la 2.1.245 sobre D6-repo-feo/gestor-pedidos, veinticuatro ejecuciones, contando el disparo en ","salida":""},{"id":"SKL-006","cap":"07 · Skills y plugins","dice":"El cuerpo de una skill no ocupa contexto hasta que se invoca: solo se paga su frontmatter, unos 208 tokens por turno.","como":"","veredicto":"REVISAR","porque":"Medido el 25-ago-2026 con la 2.1.245 sobre D6-repo-feo/gestor-pedidos, dos repeticiones por fila e idénticas al token. M","salida":""},{"id":"SKL-007","cap":"07 · Skills y plugins","dice":"description y when_to_use se cortan juntas a 1.536 caracteres: lo que pasa de ahí no llega al modelo.","como":"","veredicto":"REVISAR","porque":"Medido el 25-ago-2026 con la 2.1.245 sobre D6-repo-feo/gestor-pedidos, dos repeticiones por fila e idénticas al token. M","salida":""},{"id":"SKL-008","cap":"07 · Skills y plugins","dice":"disable-model-invocation impide que el modelo dispare la skill, y no impide invocarla por su nombre.","como":"","veredicto":"REVISAR","porque":"Medido el 25-ago-2026 con la 2.1.245 sobre D6-repo-feo/gestor-pedidos. Con la descripción que daba 8 de 8 y disable-mode","salida":""},{"id":"SKL-009","cap":"07 · Skills y plugins","dice":"Los comandos personalizados se han fusionado con las skills: un archivo en .claude/commands/ y una skill del mismo nombre crean el mismo comando, y si coinciden gana la skill.","como":"","veredicto":"REVISAR","porque":"Comprobado el 25-ago-2026 con la 2.1.245: un .claude/commands/ping-lab.md de cinco líneas responde a /ping-lab en claude","salida":""},{"id":"SKL-010","cap":"07 · Skills y plugins","dice":"Los cuatro comandos de equipo del módulo están escritos y los cuatro impiden que el modelo los dispare por su cuenta.","como":"python3 -c import sys; c=['revisar-cambio','preparar-release','postmortem','poner-al-dia']; ok=all('disable-model-invocation: true' in open(f'entregables/plantillas/skills/{n}/SKILL.md').read() for n ","veredicto":"PASA","porque":"comprobado","salida":""},{"id":"SKL-011","cap":"07 · Skills y plugins","dice":"La biblioteca de skills del módulo está escrita y documentada.","como":"test -e /home/runner/work/manual-claude-code/manual-claude-code/entregables/plantillas/skills/LEEME.md","veredicto":"PASA","porque":"existe","salida":""},{"id":"SKL-012","cap":"07 · Skills y plugins","dice":"En la plantilla de plugin, dentro de .claude-plugin solo vive plugin.json.","como":"python3 -c import os,sys; d=os.listdir('entregables/plantillas/plugin/.claude-plugin'); sys.exit(0 if d==['plugin.json'] else 1)","veredicto":"PASA","porque":"comprobado","salida":""},{"id":"SUB-001","cap":"08 · Subagentes","dice":"--agents permite definir agentes personalizados en JSON desde la propia llamada.","como":"claude --help","veredicto":"PASA","porque":"comprobado","salida":"Usage: claude [options] [command] [prompt] Claude Code - starts an interactive session by default, use -p/--print for non-interactive output Arguments: prompt Y"},{"id":"SUB-002","cap":"08 · Subagentes","dice":"claude agents gestiona los agentes que corren en segundo plano.","como":"claude agents --help","veredicto":"PASA","porque":"comprobado","salida":"Usage: claude agents [options] Manage background agents Options: --add-dir <directory> Additional directory to allow tool access to in dispatched sessions (repe"},{"id":"SUB-003","cap":"08 · Subagentes","dice":"--background arranca la sesión como agente en segundo plano y devuelve el control.","como":"claude --help","veredicto":"PASA","porque":"comprobado","salida":"Usage: claude [options] [command] [prompt] Claude Code - starts an interactive session by default, use -p/--print for non-interactive output Arguments: prompt Y"},{"id":"SUB-004","cap":"08 · Subagentes","dice":"--agent hace que la sesión entera tome el prompt de sistema, las herramientas y el modelo de un agente.","como":"claude --help","veredicto":"PASA","porque":"comprobado","salida":"Usage: claude [options] [command] [prompt] Claude Code - starts an interactive session by default, use -p/--print for non-interactive output Arguments: prompt Y"},{"id":"SUB-005","cap":"08 · Subagentes","dice":"claude plugin validate revisa un directorio de agentes y avisa del archivo cuyo frontmatter no parsea, sin abrir sesión ni gastar tokens.","como":"claude plugin validate entregables/plantillas/agents","veredicto":"PASA","porque":"comprobado","salida":"Validating components in: /home/runner/work/manual-claude-code/manual-claude-code/entregables/plantillas/agents ✔ Validation passed"},{"id":"SUB-006","cap":"08 · Subagentes","dice":"Los seis subagentes con contrato del libro existen y son legibles.","como":"test -e /home/runner/work/manual-claude-code/manual-claude-code/entregables/plantillas/agents","veredicto":"PASA","porque":"existe","salida":""},{"id":"SUB-007","cap":"08 · Subagentes","dice":"Cada uno de los seis contratos declara name y description, que es lo único obligatorio y lo único sin lo cual Claude Code se salta el archivo en silencio.","como":"python3 -c import pathlib,sys,re; ts=[p.read_text(encoding='utf-8') for p in pathlib.Path('entregables/plantillas/agents').glob('*.md')]; con=[t for t in ts if re.search('^name:', t, re.M)]; ok=[t for","veredicto":"PASA","porque":"comprobado","salida":""},{"id":"SUB-008","cap":"08 · Subagentes","dice":"El contrato del revisor obliga a contestar tres preguntas, que es la única variable que en la medición del módulo destapó los fallos 3 y 4.","como":"python3 -c import pathlib,sys; t=pathlib.Path('entregables/plantillas/agents/revisor.md').read_text(encoding='utf-8'); sys.exit(0 if ('Criterio de aceptacion' in t or 'Criterio de aceptación' in t) an","veredicto":"PASA","porque":"comprobado","salida":""},{"id":"SUB-009","cap":"08 · Subagentes","dice":"El laboratorio documenta su subagente en AGENTES.md, incluido lo que el CLAUDE.md le impide reportar.","como":"test -e /home/runner/work/manual-claude-code/manual-claude-code/D6-repo-feo/gestor-pedidos/AGENTES.md","veredicto":"PASA","porque":"existe","salida":""},{"id":"SUB-010","cap":"08 · Subagentes","dice":"La jerarquía de memoria del proyecto llega al subagente, así que una frase del CLAUDE.md que declare unos fallos ya inventariados los borra del informe del revisor.","como":"","veredicto":"REVISAR","porque":"Medido el 26-ago-2026 con la 2.1.246 y Sonnet 5, quitando del CLAUDE.md del laboratorio el último párrafo y solo ese, 30","salida":""},{"id":"SUB-011","cap":"08 · Subagentes","dice":"Declarar un subagente cuesta unos 105 tokens de entrada por turno, y su prompt de sistema cuesta cero mientras no se invoque.","como":"","veredicto":"OMITIDO","porque":"gasta tokens, se ejecuta solo con --con-coste","salida":""},{"id":"CID-001","cap":"09 · Git, CI e IDE","dice":"-p ejecuta Claude Code sin sesión interactiva, que es la base de cualquier uso en CI.","como":"claude --help","veredicto":"PASA","porque":"comprobado","salida":"Usage: claude [options] [command] [prompt] Claude Code - starts an interactive session by default, use -p/--print for non-interactive output Arguments: prompt Y"},{"id":"CID-002","cap":"09 · Git, CI e IDE","dice":"claude ultrareview lanza una revisión multiagente de la rama actual.","como":"claude --help","veredicto":"PASA","porque":"comprobado","salida":"Usage: claude [options] [command] [prompt] Claude Code - starts an interactive session by default, use -p/--print for non-interactive output Arguments: prompt Y"},{"id":"CID-003","cap":"09 · Git, CI e IDE","dice":"claude import trae la configuración de otro agente de codificación.","como":"claude --help","veredicto":"PASA","porque":"comprobado","salida":"Usage: claude [options] [command] [prompt] Claude Code - starts an interactive session by default, use -p/--print for non-interactive output Arguments: prompt Y"},{"id":"CID-004","cap":"09 · Git, CI e IDE","dice":"--bare salta hooks, sincronización de plugins, auto memoria y el descubrimiento de CLAUDE.md, y en ese modo la autenticación es estrictamente ANTHROPIC_API_KEY o apiKeyHelper.","como":"claude --help","veredicto":"PASA","porque":"comprobado","salida":"Usage: claude [options] [command] [prompt] Claude Code - starts an interactive session by default, use -p/--print for non-interactive output Arguments: prompt Y"},{"id":"CID-005","cap":"09 · Git, CI e IDE","dice":"--max-budget-usd pone un tope de gasto por ejecución, y solo funciona junto a --print.","como":"claude --help","veredicto":"PASA","porque":"comprobado","salida":"Usage: claude [options] [command] [prompt] Claude Code - starts an interactive session by default, use -p/--print for non-interactive output Arguments: prompt Y"},{"id":"CID-006","cap":"09 · Git, CI e IDE","dice":"--json-schema obliga a que la respuesta case con un esquema, que es lo que convierte una puerta de calidad en un booleano en vez de un párrafo.","como":"claude --help","veredicto":"PASA","porque":"comprobado","salida":"Usage: claude [options] [command] [prompt] Claude Code - starts an interactive session by default, use -p/--print for non-interactive output Arguments: prompt Y"},{"id":"CID-007","cap":"09 · Git, CI e IDE","dice":"--worktree abre la sesión en un árbol de trabajo de git nuevo, y --background la arranca como agente en segundo plano y devuelve el control.","como":"claude --help","veredicto":"PASA","porque":"comprobado","salida":"Usage: claude [options] [command] [prompt] Claude Code - starts an interactive session by default, use -p/--print for non-interactive output Arguments: prompt Y"},{"id":"CID-008","cap":"09 · Git, CI e IDE","dice":"claude ultrareview acepta --json, --timeout y --post, así que se puede lanzar desde un script y esperar sus hallazgos.","como":"claude ultrareview --help","veredicto":"PASA","porque":"comprobado","salida":"Usage: claude ultrareview [options] [target] Run a cloud-hosted multi-agent code review of the current branch (or a PR number / base branch) and print the findi"},{"id":"CID-009","cap":"09 · Git, CI e IDE","dice":"El laboratorio tiene un flujo de CI con tres trabajos, y el trabajo que gasta dinero depende de los dos que no gastan.","como":"python3 -c import re,sys,pathlib; t=pathlib.Path('D6-repo-feo/gestor-pedidos/.github/workflows/ci.yml').read_text(encoding='utf-8').split('jobs:')[-1]; j=re.findall('^  ([a-z]+):$', t, re.M); sys.exit","veredicto":"PASA","porque":"comprobado","salida":""},{"id":"CID-010","cap":"09 · Git, CI e IDE","dice":"El requirements.txt del laboratorio fija el cierre completo: quince paquetes, todos con versión exacta, frente a las tres líneas sin versión que tenía antes.","como":"python3 -c import sys,pathlib; ls=[l.strip() for l in pathlib.Path('D6-repo-feo/gestor-pedidos/requirements.txt').read_text(encoding='utf-8').splitlines()]; ls=[l for l in ls if l and not l.startswith","veredicto":"PASA","porque":"comprobado","salida":""},{"id":"CID-011","cap":"09 · Git, CI e IDE","dice":"El laboratorio conserva pruebas de caracterización que fijan por escrito un comportamiento que está mal en vez de arreglarlo, y las que fijaban un fallo ya arreglado se sustituyeron en vez de editarse.","como":"python3 -c import re,sys,pathlib; t=pathlib.Path('D6-repo-feo/gestor-pedidos/tests/test_caracterizacion.py').read_text(encoding='utf-8'); sys.exit(0 if len(re.findall('^def test_', t, re.M))==11 and l","veredicto":"PASA","porque":"comprobado","salida":""},{"id":"CID-012","cap":"09 · Git, CI e IDE","dice":"En modo --bare, una máquina autenticada por suscripción termina con is_error true y Authentication error en el campo result, porque bare no lee ni OAuth ni el llavero.","como":"","veredicto":"REVISAR","porque":"Medido el 27-ago-2026 con la 2.1.247 en el sandbox de la rutina, autenticado por token de suscripción y sin ANTHROPIC_AP","salida":""},{"id":"CID-013","cap":"09 · Git, CI e IDE","dice":"Cuando --max-budget-usd corta una ejecución, la salida JSON no trae campo result, así que un consumidor que solo lea result recibe null sin que nada falle.","como":"","veredicto":"OMITIDO","porque":"gasta tokens, se ejecuta solo con --con-coste","salida":""},{"id":"CID-014","cap":"09 · Git, CI e IDE","dice":"El laboratorio documenta su CI en CI.md, incluido lo que esa CI no cubre.","como":"test -e /home/runner/work/manual-claude-code/manual-claude-code/D6-repo-feo/gestor-pedidos/CI.md","veredicto":"PASA","porque":"existe","salida":""},{"id":"CID-015","cap":"09 · Git, CI e IDE","dice":"Una ejecución con -p carga y ejecuta los hooks del .claude/settings.json del repositorio aunque nadie haya aceptado el diálogo de confianza, mientras que las entradas de permissions.allow del mismo archivo se ignoran.","como":"","veredicto":"REVISAR","porque":"Medido el 27-ago-2026 con la 2.1.247 sobre D6-repo-feo/gestor-pedidos, dos ejecuciones idénticas: con --output-format st","salida":""},{"id":"CID-016","cap":"09 · Git, CI e IDE","dice":"Un servidor MCP mal declarado no rompe la ejecución: se salta, la ejecución termina limpia, y el único rastro está en mcp_server_errors del evento de inicio.","como":"","veredicto":"OMITIDO","porque":"gasta tokens, se ejecuta solo con --con-coste","salida":""},{"id":"SEG-003","cap":"10 · Seguridad y costes","dice":"La misma inyección que pasa desapercibida en un archivo del repositorio se denuncia cuando llega dentro de la respuesta de una herramienta.","como":"","veredicto":"REVISAR","porque":"Medido el 31-ago-2026 con la 2.1.251, dos repeticiones, sobre una copia de gestor-pedidos fuera del repositorio. Se mont","salida":""},{"id":"SEG-004","cap":"10 · Seguridad y costes","dice":"Una regla deny sobre la carpeta de secretos bloquea la lectura y el agente lo dice, sin depender de su criterio.","como":"","veredicto":"OMITIDO","porque":"gasta tokens, se ejecuta solo con --con-coste","salida":""},{"id":"SEG-005","cap":"10 · Seguridad y costes","dice":"Las transcripciones de sesión se guardan en claro en la máquina, y un secreto leído del código acaba copiado en ellas tantas veces como rutas de trabajo haya.","como":"","veredicto":"REVISAR","porque":"Medido el 31-ago-2026 al cerrar el laboratorio del módulo 10. La clave de pasarela sembrada en app.py aparecía 35 veces ","salida":""},{"id":"SEG-006","cap":"10 · Seguridad y costes","dice":"WebFetch envía el nombre de host a api.anthropic.com antes de descargar una URL, con cualquier proveedor de modelo.","como":"curl -sS --max-time 25 https://code.claude.com/docs/en/data-usage.md","veredicto":"PASA","porque":"comprobado","salida":"> ## Documentation Index > Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt > Use this file to discover all available pages befo"},{"id":"CST-001","cap":"10 · Seguridad y costes","dice":"Una llamada mínima con un CLAUDE.md grande ya consume decenas de miles de tokens de entrada.","como":"","veredicto":"OMITIDO","porque":"gasta tokens, se ejecuta solo con --con-coste","salida":""},{"id":"CST-002","cap":"10 · Seguridad y costes","dice":"El grueso de la entrada que pagas se sirve desde la caché, y la entrada realmente nueva es una fracción despreciable.","como":"","veredicto":"REVISAR","porque":"Medido el 31-ago-2026 con la 2.1.251 sumando las 21 ejecuciones del laboratorio del módulo 10, todas con --output-format","salida":""},{"id":"CST-003","cap":"10 · Seguridad y costes","dice":"El coste de una sola ejecución no significa nada: la misma pregunta sobre el mismo repositorio costó catorce veces más con la caché fría que con la caché caliente.","como":"","veredicto":"REVISAR","porque":"Medido el 31-ago-2026 con la 2.1.251. Ocho repeticiones de la misma pregunta neutra sobre gestor-pedidos con herramienta","salida":""},{"id":"TRB-001","cap":"11 · Troubleshooting","dice":"claude update comprueba e instala actualizaciones.","como":"claude --help","veredicto":"PASA","porque":"comprobado","salida":"Usage: claude [options] [command] [prompt] Claude Code - starts an interactive session by default, use -p/--print for non-interactive output Arguments: prompt Y"},{"id":"TRB-002","cap":"11 · Troubleshooting","dice":"Borrar la caché de proyecto no borra la configuración de usuario.","como":"","veredicto":"REVISAR","porque":"Requiere una máquina limpia. Se comprueba en la revisión trimestral.","salida":""},{"id":"TRB-003","cap":"11 · Troubleshooting","dice":"El CLI puede escribir su registro de depuración a un archivo, que es la única vía de depurar en modo no interactivo.","como":"claude --help","veredicto":"PASA","porque":"comprobado","salida":"Usage: claude [options] [command] [prompt] Claude Code - starts an interactive session by default, use -p/--print for non-interactive output Arguments: prompt Y"},{"id":"TRB-004","cap":"11 · Troubleshooting","dice":"En modo no interactivo, --debug no imprime nada de depuración por ningún canal, mientras que --debug-file escribe el registro completo.","como":"","veredicto":"REVISAR","porque":"Medido el 1-sep-2026 con la 2.1.252 sobre D6-repo-feo/gestor-pedidos, dos repeticiones por fila. La misma petición trivi","salida":""},{"id":"TRB-005","cap":"11 · Troubleshooting","dice":"Un hook PreToolUse que sale con código 1 no bloquea la herramienta y no deja rastro en el JSON de resultado; uno que sale con 2 bloquea y aparece en permission_denials.","como":"","veredicto":"REVISAR","porque":"Medido el 1-sep-2026 con la 2.1.252 sobre una copia del laboratorio en /tmp, dos repeticiones por fila, con un hook de t","salida":""},{"id":"TRB-006","cap":"11 · Troubleshooting","dice":"El valor console de OTEL_LOGS_EXPORTER se descarta sin error: la telemetría queda activada, con cero exportadores creados, y los eventos se tiran.","como":"","veredicto":"REVISAR","porque":"Medido el 1-sep-2026 con la 2.1.252, dos repeticiones por valor, leyendo el archivo de --debug-file. Con OTEL_LOGS_EXPOR","salida":""},{"id":"TRB-007","cap":"11 · Troubleshooting","dice":"claude doctor sale con código 0 aunque encuentre avisos, así que un script que decida por el código de salida da por sana una instalación con problemas.","como":"","veredicto":"REVISAR","porque":"Medido el 1-sep-2026 con la 2.1.252, dos repeticiones. La salida termina en 3 warnings found, con el método de instalaci","salida":""},{"id":"TRB-008","cap":"11 · Troubleshooting","dice":"La documentación oficial mantiene un catálogo de errores indexado por mensaje literal, y pasa del centenar de entradas.","como":"bash -c D=$(curl -fsS --max-time 25 https://code.claude.com/docs/en/errors.md 2>/dev/null) || { echo SIN-RED; exit 0; }; printf \"%s\" \"$D\" | grep -c \"^### \"","veredicto":"PASA","porque":"comprobado","salida":"107"},{"id":"TRB-009","cap":"11 · Troubleshooting","dice":"El except desnudo que se tragaba los fallos de aviso ya no existe en el laboratorio.","como":"bash -c grep -nE \"except:\\s*($|#)\" app.py; test $? -eq 1","veredicto":"PASA","porque":"comprobado","salida":""},{"id":"TRB-010","cap":"11 · Troubleshooting","dice":"El laboratorio tiene una prueba que se pone roja si alguien vuelve a tragarse un error sin traza.","como":"test -e /home/runner/work/manual-claude-code/manual-claude-code/D6-repo-feo/gestor-pedidos/tests/test_diagnostico.py","veredicto":"PASA","porque":"existe","salida":""},{"id":"TRB-011","cap":"11 · Troubleshooting","dice":"El aviso de que los permisos allow del proyecto se están ignorando sale solo por la salida de error, y no aparece en ninguno de los campos del JSON de resultado.","como":"","veredicto":"REVISAR","porque":"Medido el 1-sep-2026 con la 2.1.252 sobre el laboratorio, cinco repeticiones. El JSON de --output-format json trae 26 ca","salida":""},{"id":"TRB-012","cap":"11 · Troubleshooting","dice":"El modo seguro descarta la configuración del proyecto pero no silencia el aviso de confianza del espacio de trabajo.","como":"","veredicto":"REVISAR","porque":"Medido el 1-sep-2026 con la 2.1.252 sobre el laboratorio, dos repeticiones por fila, con la misma petición trivial. Sin ","salida":""},{"id":"TRB-013","cap":"11 · Troubleshooting","dice":"Cuando ves un error del CLI, los reintentos que le tocaban ya se han hecho.","como":"bash -c D=$(curl -fsS --max-time 25 https://code.claude.com/docs/en/errors.md 2>/dev/null) || { echo SIN-RED; exit 0; }; printf \"%s\" \"$D\" | grep -c \"retries transient failures up to 10 times\"","veredicto":"PASA","porque":"comprobado","salida":"1"},{"id":"REPO-001","cap":"00 · Gobierno del proyecto","dice":"El repositorio del manual está público, como se decidió el 13 de agosto de 2026.","como":"curl -sS --max-time 25 -o /dev/null -w %{http_code} https://github.com/julian-najas/manual-claude-code","veredicto":"PASA","porque":"comprobado","salida":"200"},{"id":"REPO-002","cap":"00 · Gobierno del proyecto","dice":"El companion público sigue publicado: si dejara de serlo, GitHub Pages dejaría de servir las 156 páginas.","como":"curl -sS --max-time 25 -o /dev/null -w %{http_code} https://github.com/julian-najas/claude-code-companion","veredicto":"PASA","porque":"comprobado","salida":"200"},{"id":"REPO-003","cap":"00 · Gobierno del proyecto","dice":"El sitio del companion responde y sirve el índice por síntoma.","como":"curl -sS --max-time 25 https://julian-najas.github.io/claude-code-companion/","veredicto":"PASA","porque":"comprobado","salida":"<!doctype html><html lang=\"es\"><head><meta charset=\"utf-8\"> <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"> <title>Índice por síntoma · Clau"},{"id":"SEG-001","cap":"10 · Seguridad y costes","dice":"rm -rf sobre el directorio del proyecto lo destruye sin confirmación del sistema operativo.","como":"","veredicto":"OMITIDO","porque":"prueba destructiva, documentada pero nunca ejecutada","salida":""},{"id":"CAS-001","cap":"12 · Casos completos","dice":"procesar_pedido() ya no hace ocho cosas: las siete responsabilidades están extraídas a funciones propias y el endpoint solo las ordena.","como":"bash -c grep -c \"^def \\(validar\\|subtotal_lineas\\|aplicar_iva\\|descuento_cliente_antiguo\\|guardar\\|notificar\\|facturar\\)(\" app.py","veredicto":"PASA","porque":"comprobado","salida":"7"},{"id":"CAS-002","cap":"12 · Casos completos","dice":"En el laboratorio ya no hay tipo de IVA por defecto: los tipos viven en una tabla por país y lo que no está en ella se rechaza.","como":"bash -c grep -q \"IVA_POR_PAIS\" config.py && grep -q \"raise PaisSinTipoDeIVA\" app.py && echo OK","veredicto":"PASA","porque":"comprobado","salida":"OK"},{"id":"CAS-003","cap":"12 · Casos completos","dice":"El laboratorio tiene un sitio con nombre donde consta qué se decidió, por qué, y qué sigue sin decidirse.","como":"test -e /home/runner/work/manual-claude-code/manual-claude-code/D6-repo-feo/gestor-pedidos/DECISIONES.md","veredicto":"PASA","porque":"existe","salida":""},{"id":"CAS-004","cap":"12 · Casos completos","dice":"Cuando la decisión de negocio no está escrita en el repositorio, el agente la toma él para poder terminar la tarea.","como":"","veredicto":"REVISAR","porque":"Medido el 2-sep-2026 con la 2.1.258 sobre copias del laboratorio en /tmp, con herramientas Read, Glob, Grep, Edit y Writ","salida":""},{"id":"CAS-005","cap":"12 · Casos completos","dice":"Una frase que prohíbe tomar la decisión de negocio hace que el agente no edite nada, y además sale más barata que dejarle terminar.","como":"","veredicto":"REVISAR","porque":"Medido el 2-sep-2026 con la 2.1.258 sobre copias del laboratorio, dos repeticiones por fila, misma petición y mismas her","salida":""},{"id":"CAS-006","cap":"12 · Casos completos","dice":"Una refactorización que solo mueve código es repetible entre ejecuciones y conserva el comportamiento; la que necesita una decisión, no.","como":"","veredicto":"REVISAR","porque":"Medido el 2-sep-2026 con la 2.1.258, dos repeticiones por tarea sobre copias limpias del laboratorio. Desmontar procesar","salida":""},{"id":"CAS-007","cap":"12 · Casos completos","dice":"Una refactorización correcta que conserva el comportamiento puede arrancar la costura por la que las pruebas agarran la aplicación, y entonces no hay fallos sino errores de montaje.","como":"","veredicto":"REVISAR","porque":"Medido el 2-sep-2026 con la 2.1.258. Misma petición dos veces, unificar config.py y settings.py, sobre copias limpias. L","salida":""},{"id":"CAS-008","cap":"12 · Casos completos","dice":"El repositorio del laboratorio ya no tiene dos archivos de configuración que se contradigan: settings.py no existe y config.py se importa de verdad.","como":"bash -c test ! -e settings.py && grep -q \"^import config\" app.py && echo OK","veredicto":"PASA","porque":"comprobado","salida":"OK"},{"id":"CAS-009","cap":"12 · Casos completos","dice":"Las pruebas del laboratorio cubren los cuatro países de la tabla de IVA, más el país que no está en ella y el pedido sin país.","como":"bash -c grep -c \"^def test_\" tests/test_caracterizacion.py","veredicto":"PASA","porque":"comprobado","salida":"11"},{"id":"CAS-010","cap":"12 · Casos completos","dice":"La decisión sobre si los dos descuentos por cantidad se acumulan está tomada y escrita, y su prueba ya no dice hoy.","como":"bash -c grep -q \"DESCUENTO_MAS_DE_100\" config.py && grep -q \"^def test_los_dos_descuentos_de_cantidad_se_acumulan_a_proposito\" tests/test_caracterizacion.py && echo OK","veredicto":"PASA","porque":"comprobado","salida":"OK"},{"id":"CAS-011","cap":"12 · Casos completos","dice":"La receta oficial de refactorización pide conservar el comportamiento, y la de pruebas termina en un paso que sobre una red de caracterización es el movimiento equivocado.","como":"bash -c D=$(curl -fsS --max-time 25 https://code.claude.com/docs/en/common-workflows.md 2>/dev/null) || { echo SIN-RED; exit 0; }; printf \"%s\" \"$D\" | grep -c \"maintaining the same behavior\"","veredicto":"PASA","porque":"comprobado","salida":"1"},{"id":"CAS-012","cap":"12 · Casos completos","dice":"Lo que el módulo 12 añade al repositorio cuesta diecisiete tokens por turno, y el archivo de decisiones no cuesta ninguno.","como":"","veredicto":"REVISAR","porque":"Medido el 2-sep-2026 con la 2.1.258 sobre el laboratorio, dos repeticiones por fila con la misma petición trivial y las ","salida":""}]}