Cuando un agente escribe código que no compila, el diagnóstico es fácil. El problema son las otras veces: cuando escribe código que compila, parece razonable y está apoyado en una suposición falsa sobre tu proyecto. Eso no se arregla con mejores instrucciones, se arregla dándole forma de comprobar las cosas. Vamos con de dónde salen realmente esos errores y qué contexto hay que darle para que dejen de producirse. Los ejemplos van sobre un proyecto Laravel, que es donde nos movemos en AndorraDev, pero la idea de darle al agente forma de verificar en vez de deducir sirve con cualquier lenguaje y cualquier agente.
Un agente se inventa cosas cuando tiene que deducirlas
Es útil dejar de pensar en "alucinaciones" y empezar a pensar en deducciones sin verificar.
Cuando le pides a un agente que trabaje sobre tu proyecto y no tiene forma de comprobar algo, no se para: rellena el hueco con lo más probable. Lee una migración de hace dos años y concluye que la columna existe. Ve un modelo con una relación declarada y da por hecho que la clave foránea está puesta. Encuentra un método en la documentación de un paquete y lo llama, aunque la versión que tienes instalada lo renombrara.
Ninguna de esas tres cosas es un fallo de razonamiento. Son inferencias razonables a partir de información incompleta, y cada una produce código que parece correcto y no lo es.
La forma de reducirlas no es pedirle que no invente. Es darle manera de comprobar.
Hay tres niveles de certeza y conviene saber en cuál estás
Toda la cuestión se ordena bastante bien si separas de dónde sale cada dato.
Nivel uno: lo que un humano escribió. El fichero de contexto del proyecto, el README, los comentarios. Es lo más barato de leer y lo menos fiable, porque lo escribió alguien que podía estar equivocado y porque envejece en silencio. Nadie recibe un aviso cuando el código deja de parecerse al documento que lo describe.
Nivel dos: lo que dice el código. Las migraciones, los modelos, las rutas declaradas. Es verificable y es lo que el proyecto pretende ser. Sigue siendo una inferencia: una migración te dice qué se intentó crear, no qué hay en la base de datos hoy.
Nivel tres: lo que hay de verdad. El esquema real consultado, el número de filas, las rutas que el enrutador tiene registradas, los últimos errores del registro. Es lo más caro de obtener y es lo único que no admite discusión.
La mayoría de las configuraciones se quedan en el nivel uno, que es justo el que más se degrada.
El fichero de contexto que escribiste hace seis meses ya miente
Casi todo el mundo empieza y termina por aquí: un fichero de instrucciones en la raíz del repositorio con lo que el agente debería saber. Es lo correcto para las convenciones, y es exactamente lo peor posible para cualquier cosa que el código pueda cambiar sin avisar a ese fichero.
Un fichero de instrucciones del proyecto es cómodo: está siempre presente, no hay que invocar nada y describe convenciones que el código no expresa. Para eso es insustituible.
El problema es cuando se convierte en la fuente de verdad sobre el esquema o la arquitectura. Entonces pasan tres cosas a la vez. Se paga en cada turno de cada sesión, porque ocupa contexto siempre. Se desactualiza sin avisar, en cuanto alguien renombra una columna y no toca el documento. Y afirma con la misma confianza lo cierto y lo obsoleto, porque no hay nada que lo contraste.
En proyectos grandes es fácil que ese fichero pase de los doscientos kilobytes. A ese tamaño ya no es una ayuda: nadie lo revisa entero, y buena parte describe código que cambió hace meses.
La desviación de esquema es el ejemplo perfecto
Si hay un caso que ilustra la diferencia entre deducir y comprobar, es este.
Coges el esquema real de la base de datos y reconstruyes el esquema esperado a partir de las migraciones. Luego los comparas, y las dos direcciones del desajuste significan cosas distintas:
Una columna que está en la base de datos y no en ninguna migración es un aviso. Alguien la creó a mano, o quedó de una migración borrada. El código no sabe que existe.
Una columna que está en una migración y no en la base de datos es crítico. Significa que esa migración no se ha ejecutado donde estás mirando, y cualquier código que la use va a fallar.
Un agente que solo lee migraciones no puede distinguir esos dos casos, porque solo ve un lado. Y ese es exactamente el tipo de cosa sobre la que va a escribir código con toda la confianza del mundo.
Lo mismo vale para las claves foráneas huérfanas. Una migración te dice que la relación está declarada; solo un recuento contra los datos reales te dice que hay nueve filas apuntando a un registro que ya no existe. La consulta es una línea y convierte una sospecha en un hecho, que es justo la diferencia entre un informe que sirve y uno que hay que verificar a mano.
Deriva la API del código instalado, no de la documentación
La documentación de un paquete describe una versión, y no necesariamente la tuya. Un agente que la consulta está leyendo sobre un proyecto parecido al tuyo, mientras que la respuesta exacta está a un ls de distancia, dentro de vendor.
Cuando el agente va a usar un paquete, la pregunta no es "qué dice la documentación de este paquete", es "qué versión tengo instalada y qué expone". Y la respuesta está en el disco:
El fichero de bloqueo manda sobre todo lo demás. El composer.json del propio paquete puede no llevar versión, y una rama de desarrollo no te dice nada. La versión resuelta en el proyecto que lo consume es la autoridad.
Y el código instalado manda sobre el fichero de bloqueo. Un alias de rama puede mentir sobre la versión; la presencia o ausencia de un fichero, no. Comprobar que existe la clase que vas a usar es más fiable que comprobar el número de versión.
Llevado al extremo, el patrón que más me gusta es derivar los nombres disponibles aplicando la misma regla que aplica el framework. Si sabes que una herramienta se nombra convirtiendo el nombre de su clase a minúsculas con guiones, listar el directorio de clases te da el conjunto exacto de la versión instalada, al margen de lo que diga cualquier documentación.
Detectar el entorno en vez de asumirlo
De los comandos que un agente propone y no funcionan, la mayoría no fallan por estar mal escritos: fallan porque asumen dónde vive la aplicación. Y eso cambia entre tu portátil, el contenedor y el servidor sin que nada en el código lo diga.
"Ejecuta esta consulta" significa cosas distintas según dónde viva la aplicación. Puede ser un docker exec a un contenedor cuyo nombre hay que averiguar, una llamada directa en local con Herd o Valet, una ruta de Windows nativo donde el escapado de la shell no es el de bash, o una sesión de WSL que parece Linux y no siempre lo es.
La regla que funciona es comprobar antes de ejecutar y, sobre todo, decir en el informe qué entorno se ha usado. Un resultado sin esa línea es un resultado que no puedes reproducir.
Separar leer de escribir, siempre
La diferencia entre una herramienta que se usa y otra que se desinstala a la semana rara vez está en lo que acierta. Está en si puedes lanzarla sin miedo la primera vez, cuando todavía no sabes si va a tocar algo que no debía.
Sin argumento, la herramienta solo informa y no toca nada. Con la orden explícita de arreglar, pide confirmación por cada cambio. Y hay un modo intermedio que enseña el diff sin aplicarlo.
Parece obvio y casi ninguna configuración lo respeta. La consecuencia de no tenerlo es que la gente deja de invocar análisis por si acaso, y entonces la herramienta no sirve de nada.
Hay un caso donde esto es especialmente importante: cualquier operación que borre ficheros. Consolidar cien migraciones en una es enormemente útil y es también la operación que puede dejarte sin historial si se ejecuta sobre el entorno equivocado.
Etiquetar la procedencia dentro del informe
Un informe automático llega como un bloque homogéneo: veinte hallazgos con el mismo aspecto y el mismo tono. Nada distingue el que se comprobó contra la base de datos del que se dedujo leyendo una migración de hace dos años, y esa distinción es justo la que decide cuál te puedes creer.
Cuando un informe mezcla hallazgos deducidos del código con hallazgos comprobados contra datos, la lista entera vale lo que vale el peor de sus elementos, porque no sabes cuál es cuál.
La solución es que cada línea diga de dónde sale:
Orphaned Foreign Keys
operations.client_seller_id -> clients.id
[WARNING] 9 orphaned rows (verified against live data)
Schema Drift
table: invoices
[WARNING] column `legacy_ref` exists in DB but not in any migration
Ese (verified against live data) es la diferencia entre un hallazgo sobre el que actúas y uno que tienes que ir a comprobar. Y la cabecera del informe debería decir en qué modo se ejecutó, para que sepas de antemano cuánta certeza estás leyendo.
Qué hay para lo mismo y en qué se diferencia
Nadie resuelve esto con una sola pieza, y las que existen no compiten entre sí: cada una cubre un tramo distinto del recorrido que va de lo que el agente supone a lo que puede comprobar. Merece verlas juntas antes de elegir.
El fichero de contexto escrito a mano es el punto de partida de todos. Coste cero, siempre presente, y es el único sitio donde caben las convenciones que el código no expresa. Su compromiso ya lo hemos visto: se paga en tokens siempre y se degrada sin avisar.
Laravel Boost resuelve el nivel tres muy bien: un servidor que expone el esquema real, las consultas, los últimos errores y la información de la aplicación, y que además escribe el contexto en disco para que esté disponible sin invocar nada. El compromiso es que es una dependencia de desarrollo en tu proyecto, con su instalación y su servidor corriendo. Y lo que da es estado, no procedimientos: sabe cómo está tu proyecto, no cómo consolidar cien migraciones.
Un servidor genérico de base de datos te da esquema real y nada más. Devuelve tablas y columnas sin las convenciones del ORM, sin saber qué es una relación polimórfica ni dónde vive el mapa de tipos.
El análisis estático clásico, del tipo Larastan o las inspecciones del IDE, es determinista y no alucina por construcción. Lo que no hace es hablar con el agente: el resultado vive en la herramienta, no en el contexto del modelo.
Lo que aporta un conjunto de procedimientos empaquetados es el otro eje: cómo hacer una tarea concreta bien, con los pasos, las fuentes que hay que consultar y el formato del informe. No compite con lo anterior, se apoya en ello. Su enfoque es recalcular en cada invocación: no deja nada en tu proyecto ni ocupa contexto cuando no lo usas, y a cambio el coste de lectura se paga cada vez. Y como son instrucciones en texto y no llamadas a función, orientan al modelo en vez de forzarlo, que es lo que las hace portables entre agentes.
Y si no usas Claude Code
Los principios no dependen de la herramienta y valen para cualquier agente que trabaje sobre tu código:
- Ordena tus fuentes por certeza y ten claro en qué nivel está cada afirmación.
- Prefiere comprobar a deducir siempre que el coste lo permita. Una consulta de recuento cuesta milisegundos y convierte una sospecha en un hecho.
- Deriva la API del código instalado, no de la documentación publicada.
- Detecta el entorno y dilo en el resultado.
- Separa el modo lectura del modo escritura con una orden explícita.
- Y etiqueta la procedencia de cada hallazgo. Un informe sin procedencia hay que verificarlo entero, con lo cual no ha ahorrado nada.
Dónde está
Es un plugin de Claude Code, no un paquete de Composer, así que no se instala con el gestor de dependencias ni entra en tu proyecto. Se añade desde el propio terminal:
/plugin marketplace add edulazaro/laraclaude
/plugin install laraclaude@edulazaro
El código está en GitHub y es MIT. Son treinta y dos procedimientos escritos en Markdown, unas siete mil líneas, sin una sola línea de PHP: analizar un modelo, auditar seguridad, consolidar migraciones, generar componentes, detectar código muerto y comprobar el estado de la aplicación contra la base de datos real.
Si estás metiendo agentes en tu flujo de desarrollo, el trabajo de verdad no está en el prompt, está en decidir de dónde sale cada dato. Lo tratamos en inteligencia artificial y desarrollo Laravel, y si nos cuentas cómo trabajas hoy te decimos qué merece la pena automatizar.