# Probar una aplicación de escritorio sin que la persona intervenga

Este informe cuenta cómo la IA (Claude Opus 5.5 en Claude Code, en Windows 11) probó AutoCAD 2027 casi sin ayuda de la persona: 244 pruebas en dos días.

Recoge:
- qué herramientas se usaron, cuáles funcionaron mejor y cuáles no;
- qué errores cometió la propia IA;
- recomendaciones para quien quiera hacer algo parecido con AutoCAD o con otra aplicación de escritorio grande, cerrada y con interfaz.

Los resultados técnicos de AutoCAD están en el [informe final de AutoCAD 2027](/posts/autocadlab-autocad-2027/informe-final-autocad-2027.md).

---

## 1. Cuánto hizo falta la persona

| Para qué | Por qué no se pudo evitar |
|---|---|
| Instalar el módulo VBA, descargar el SDK de ObjectARX y aceptar el UAC del C++ de Visual Studio | Las instalaciones y las descargas necesitan permiso explícito y, a veces, elevación |
| Crear la cuenta y la app de APS (nube) | Una cuenta con posibles costes es decisión de la persona |
| Las primeras pruebas de interfaz (cinta, paletas, *jigs*, macros de botones) | Al principio se hicieron con la persona delante, para validar qué se veía. Después casi todo se repitió sin ella (§4) |
| Comprobar a mano Hot Reload en Visual Studio (DEV-00) | Para descartar que el fallo fuera de la automatización: falló igual a mano |
| Dar permisos por tandas: ratón y teclado reales, SECURELOAD temporal, perfil con copia, instalar | Las acciones con riesgo se pidieron una vez por tanda, no prueba a prueba |
| Autorizar SHL-02 | El clasificador de permisos la había bloqueado («Security Weaken») |

Todo lo demás lo hizo la IA sola: compilar, arrancar y cerrar instancias, ejecutar pruebas, provocar y recoger caídas, cerrar diálogos, depurar con breakpoints, mirar la pantalla, y montar y ejecutar trabajos en la nube.

---

## 2. Arquitectura: un «Playwright» para AutoCAD

Una aplicación como AutoCAD no tiene modo de pruebas, así que hay que construirlo. Pieza a pieza:

1. **Runner dentro de la aplicación** (`addins/AutoCADLab.Runner`): un complemento .NET que AutoCAD carga al arrancar desde un bundle en `ApplicationPlugins`.
   - Escucha por un **named pipe** (`autocadlab-<pid>`).
   - Pasa cada orden al hilo principal mediante el `Dispatcher` de WPF.
   - Carga cada prueba en un **`AssemblyLoadContext` descargable**, a partir de una copia sombra.
   - Las pruebas son métodos con `[LabTest("ID", "descripción")]` que devuelven `LabResult.Pasa/Falla`.
2. **CLI `lab`** (`tools/Lab`, usado como `./lab.cmd`):
   - `inicio <tarea>` y `fin <tarea>` marcan una tarea y calculan sus métricas de IA leyendo el historial de Claude Code (tokens, compilaciones, ejecuciones, errores por causa);
   - `build` compila;
   - `run <ID> --pid <pid> --dll <ruta>` ejecuta una prueba en una instancia concreta y devuelve JSON;
   - `vigilar` repite una prueba cada vez que cambia la DLL.
3. **Instancias propias** (`tools/instancia.py`): arranca `acad.exe` con argumentos conocidos, espera al pipe y registra el PID en `%LOCALAPPDATA%\AutoCADLab\instancias.json`.
   - **Solo termina lo que arrancó él.** El AutoCAD de la persona no se tocó nunca.
4. **Inventarios de la API** (`tools/ApiInventory` y `pruebas/resultados/*.tsv`): por reflexión para .NET, desde los ensamblados de interop para COM, y desde la propia AutoCAD para LISP.
   - Regla para la IA: **comprobar cada símbolo con grep antes de usarlo**.
5. **Checklist con resultados** (`tools/checklist.py`): cada prueba se marca desde un JSON `{ID: [probada, "✅/⚠️/❌/💥", "observación"]}`. La observación cuenta lo medido y el control.
6. **Archivos de lote fuera del repositorio** (`%LOCALAPPDATA%\AutoCADLab\…`): así el repositorio no se ensucia, y las rutas de confianza de SECURELOAD quedan acotadas.

**Ciclo resultante:**
- editar → compilar → ver el resultado en **~2,2 s**, sin reiniciar AutoCAD;
- **~3,7 s** desde guardar el archivo, con `dotnet watch` + `lab vigilar`;
- con reinicio completo de AutoCAD (compilar, arrancar una instancia y ejecutar): 11–14 s (DEV-03).

---

## 3. Herramientas, de mejor a peor resultado

| Herramienta | Para qué | Resultado |
|---|---|---|
| **Runner + pipe + `lab run`** | Ejecutar código dentro de AutoCAD sin reiniciarlo | **Excelente.** La base de todo. Hubo que descubrir y sortear que AutoCAD retiene cada ensamblado en `ExtensionLoader.m_extensions` (DEV-07), y ahora el runner lo quita |
| **Inventarios de la API + grep** | Que la IA no invente símbolos | **Excelente.** 0 funciones LISP inventadas en LSP-15. En C++, las cabeceras del SDK hicieron de inventario |
| **`tools/instancia.py`** (instancias propias registradas) | Pruebas que tumban AutoCAD, reinicios, estado limpio | **Excelente**, con un fallo corregido: Windows reutiliza los PID y uno registrado ya era `chrome.exe`. Ahora comprueba que el proceso sea `acad.exe` |
| **Sonda de estado** (`UI-CUENTA`: entidades por tipo, `USERI1-5`/`USERS1-2`, `CMDNAMES`, quiescencia) | Saber qué pasó sin mirar la pantalla | **Excelente.** Las variables `USERS1/2` sirven de buzón: LISP, VBA, ARX o una macro escriben ahí y el runner lo lee |
| **Testigos en archivo** (`testigos\*.txt`, trazas paso a paso) | Lo asíncrono (WebView2, eventos) y lo que muere sin avisar (consola) | **Muy bueno.** La salida estándar se pierde al caer el proceso; un `File.AppendAllText` por paso, no |
| **`tools/con.py`** (`accoreconsole` con stdin cerrado, tiempo límite y log decodificado) | Pruebas de la consola | **Muy bueno**, si se usa. Lanzar la consola a mano la dejó colgada dos veces |
| **UI Automation** (`tools/ui.ps1`: `arbol`, `pulsar`, `escribir`) | Pulsar botones de diálogos WPF y Win32 sin ratón | **Bueno.** Falla con controles dibujados a mano: los botones de la cinta de un CUIx solo ofrecen Toggle, y la lista del VBA Manager no tiene nombres |
| **Mensajes de Win32** (`tools/pulsar.py`, `WM_SETTEXT`, `WM_COMMAND`, `WM_KEYDOWN` dirigidos a una ventana) | Diálogos clásicos: CER, recuperación, Guardar como, VBA Manager | **Muy bueno.** No toca el ratón ni el teclado reales y no depende del primer plano. Hay que mirar antes la clase del control (`SysListView32` no es un `ListBox`) |
| **`SendStringToExecute`** (`UI-ENVIAR`, texto en un archivo) | Escribir en la línea de comandos como un usuario | **Bueno**, con trampas: no hay eco en el historial, `FILEDIA` decide si sale un diálogo, y varios comandos en una sola cadena se encolan de forma poco predecible |
| **Capturas** (`tools/captura.py`, `PrintWindow`, con recorte) | Verificación visual: proxys, temas, cinta, PDF | **Bueno.** Detectó cosas que las aserciones no veían. Con AutoCAD ocupado, `PrintWindow` bloquea al que llama |
| **Visual Studio por DTE** (`pruebas/dev/dev08_dte.py`, `dev14_hot.py`) | Breakpoints y F5 sin la persona | **Bueno.** Breakpoint en el código de la prueba, adjuntarse a `acad.exe`, leer variables. Requiere VS invisible y paciencia con los tiempos |
| **Cliente DAP propio** (`pruebas/lisp/dap_lsp06.py`) | Depurar LISP con el adaptador de AutoCAD sin VS Code | **Bueno.** Breakpoints y variables por protocolo |
| **COM desde Python** (pywin32, `DispatchEx`) | Orquestar instancias, ObjectDBX, concurrencia | **Bueno con cuidado.** `GetActiveObject` devuelve la instancia de la persona, y `RPC_E_CALL_REJECTED` aparece disfrazado de `AttributeError` |
| **Subagentes en paralelo** | Pruebas que no necesitan AutoCAD (DWG, inventarios, análisis) | **Bueno.** Ahorran tiempo; hay que verificar lo que devuelven |
| **API REST de APS** (`pruebas/aps/aps04.py`) | Design Automation | **Muy bueno.** Funcionó a la primera; se detiene si una respuesta habla de pago |
| **`SendInput`, ratón y teclado reales** (`tools/entrada.py`) | Cuando nada más llega (menús emergentes, KeyTips) | **Último recurso.** Solo con permiso; se detiene si cambia el primer plano. La IA lo usó una vez sin permiso (registrado como error) |
| **Historial de la línea de comandos por `WM_GETTEXT`** (`tools/historial.py`) | Leer lo que respondió AutoCAD | **Poco fiable.** Con la línea de comandos oculta da texto viejo o mezclado. Mejor `CMDNAMES`, `USERS*` o los testigos |
| **Hot Reload y Editar y continuar de Visual Studio** | Recargar código sin reiniciar | **No funcionó** (DEV-00), ni automatizado ni a mano. El cargador propio lo sustituye |
| **`Application.Quit()` desde el contexto de aplicación** | Cerrar AutoCAD limpio | **No funcionó: cuelga AutoCAD.** `SendStringToExecute("_.QUIT ")` cierra limpio |

---

## 4. Técnicas que funcionaron

1. **Medir siempre con un control.** Para cada «X provoca Y» hace falta el caso que lo descarta. Ejemplos:
   - el fallo de `/al` en local no era SECURELOAD por sí solo (falló también en una ruta de confianza); eran dos condiciones juntas;
   - el «Do Not Load» de VBA vetaba **ese archivo** (otro sí mostraba el diálogo);
   - `accoreconsole` se colgaba también con un script trivial, así que el problema era cómo se lanzaba.
2. **Leer el estado, no la pantalla.** Cuentas de entidades, variables de sistema, `CMDNAMES`, archivos testigo y ventanas visibles del proceso con sus botones (`instancia.py ventanas`). La captura de pantalla, para confirmar.
3. **Hacer las pruebas peligrosas en una instancia desechable.** Así se pueden provocar las 5 caídas 💥 sin miedo. Después:
   - cerrar CER **sin enviar** (`WM_CLOSE`);
   - contestar al diálogo «Drawing Recovery» del siguiente arranque.
4. **Los diálogos modales son el enemigo número uno del lote.** Bloquean el pipe, COM y el arranque. El aviso de proxys tuvo 180 s parado el arranque de una instancia.
   - Hay que conocer sus variables (`FILEDIA`, `CMDDIA`, `EXPERT`, `PROXYNOTICE`, `SECURELOAD`).
   - Tener siempre un vigilante que liste las ventanas del proceso mientras corre una prueba.
5. **Lanzar en segundo plano lo que puede abrir un diálogo.** La prueba sigue corriendo en segundo plano, y en primer plano se mira si apareció una ventana y se pulsa el botón elegido. Así se midió SECURELOAD con VBA (Do Not Load / Load Once).
6. **Pedir los permisos por tandas y con alcance explícito,** sin extenderlos solos de una tanda a otra.
7. **Escribir las observaciones con el control incluido.** Corregir las mal clasificadas: APS-02 pasó de ❌ a ✅ cuando apareció la causa real, y la descarga de contextos de DEV-07 se había dado por buena con una métrica falsa.

---

## 5. Lo que no funcionó o costó caro

- **Las trampas del intérprete de órdenes de Windows desde Bash y Python:**
  - los heredocs y el texto de Python convierten `\\`, `\n`, `\a` en rutas y en cadenas de LISP; la IA repitió este error varias veces;
  - PowerShell 5.1 escribe en ANSI sin `-Encoding`;
  - la herramienta Write quita los espacios finales, que en un `.scr` son Enter.
  - **Solución:** escribir los archivos con el editor o con Python sin escapes, y con barras `/` en las rutas.
- **Usar mal herramientas propias:**
  - `lab build addins/AutoCADLab.UI` compiló otro proyecto, porque el primer argumento era el id de la tarea; ahora lo rechaza;
  - lanzar `accoreconsole` sin `tools/con.py`.
  - **Solución:** que las herramientas validen sus argumentos y que la IA lea su cabecera de uso antes.
- **Suponer que existe algo sin comprobarlo.** `VBASAVE` costó 4 intentos. Con COM o comandos no hay inventario: `(getcname "CMD")` lo resuelve en un segundo.
- **El clasificador de permisos de Claude Code** bloqueó con razón `SECURELOAD=0` y `new Database()` fuera de AutoCAD. Hay que plantearlo a la persona, no rodearlo.
- **La ventana de la persona:** COM y la ROT apuntan a su AutoCAD si está abierto. Una herramienta descuidada puede escribir en su dibujo.

---

## 6. Errores de la IA registrados

Errores de la IA registrados (en las métricas y en `docs/estado-y-pendientes.md`):
- **Símbolos inventados o mal escritos (A1):** pocos; los detectaron el compilador y el inventario.
- **Escapes en rutas y cadenas:** recurrente.
- **Una vez usó el teclado real sin permiso.**
- **Herramientas mal usadas:** `--passive` sin elevación (instalador 5007), `lab build` con una ruta y la consola lanzada a mano.
- **Suposiciones sobre el entorno:** `VBASAVE`, `RunMacro` con el nombre corto, el primer diagnóstico de la descarga de contextos.
- **Resultados dados por buenos sin mirar:** EXP-02 aceptaba un PDF sin mirar su contenido; ahora se renderiza y se comprueba.

La lección común: **la IA acierta mucho con el lenguaje y falla en el entorno.** Las barreras útiles son las que comprueban el entorno de forma barata y automática: inventarios, `getcname`, validar argumentos y controles.

---

## 7. Recomendaciones para quien quiera hacer algo parecido

**Antes de empezar**
1. Fijar el entorno (versión, idioma, SDK) y no actualizarlo a mitad.
2. Escribir un `CLAUDE.md` con el alcance, las reglas y lo que exige permiso: instalar, la nube, el dinero, la licencia, la sesión de la persona.
3. **Inventariar la API desde el primer día** y obligar a consultarla antes de usar un símbolo.
4. Decidir las rutas de confianza y las carpetas de trabajo fuera del repositorio.

**La infraestructura**

5. Construir un **runner dentro de la aplicación**, con pipe, contexto descargable y copia sombra. Es la inversión que más rinde: pasar de 11–14 s (con reinicio) a 2,2 s por iteración cambia todo. Medir de verdad si descarga, contando copias del ensamblado y no con `AssemblyLoadContext.All`.
6. **Instancias propias registradas.** No usar `GetActiveObject` ni matar por nombre de proceso, y comprobar el ejecutable antes de terminar un PID.
7. Una **sonda de estado** barata (variables, cuentas, comando activo) y **testigos en archivo** para lo asíncrono y lo que muere.
8. Un **vigilante de diálogos**: listar las ventanas del proceso mientras corre cada prueba, y botoneras por mensajes de Win32 o UI Automation para cerrarlas.
9. Un lanzador para la versión sin interfaz, si existe (aquí `accoreconsole`), que cierre stdin y ponga tiempo límite.
10. Las métricas de la IA, automáticas (`lab inicio/fin`), con una taxonomía de fallos.

**Al probar**

11. Cada afirmación, con su control. Marcar como verificado solo lo ejecutado.
12. Lo peligroso, en una instancia desechable, recogiendo CER o el equivalente **sin enviar** informes.
13. Preferir, por este orden:
    1. API o estado interno;
    2. mensajes de Win32 a una ventana concreta;
    3. UI Automation;
    4. ratón y teclado reales, solo con permiso y comprobando el primer plano.
14. Para la interfaz, hacer una primera pasada con la persona delante, que diga qué ve, y después automatizar lo validado.
15. Los permisos de riesgo, por tandas y con alcance explícito. Si un clasificador bloquea algo, preguntar a la persona.
16. Registrar los errores propios, corregir las clasificaciones equivocadas y no dar por resuelto lo no verificado.

**Qué no repetir**
- Rutas con barras invertidas dentro de heredocs o de cadenas de Python.
- Herramientas propias sin validar sus argumentos.
- Suponer que existe un comando o una API sin una comprobación de un segundo.
- Fiarse del historial de la interfaz como fuente de verdad: mejor el estado y los testigos.
