La corrección que me convencí de no hacer
Hace un tiempo construí un arnés de WebDriver para Greenhouse para que un agente pudiera hacer clic a través de la app real durante la verificación, con IPC real, SQLite real, sin simulaciones. Funciona bien. También tiene un efecto secundario molesto: cada vez que se abre una sesión, la ventana de la app salta al frente de mi pantalla y me roba el foco del teclado. Si en ese momento exacto estoy escribiendo cualquier otra cosa, esas teclas van a parar a la ventana equivocada.
Puse a un agente a corregirlo, y su primera pasada de investigación terminó por convencerse de no corregirlo en absoluto. La configuración de ventana de Tauri tiene un campo focus, pero esa es una limitación conocida y sin resolver en la biblioteca de manejo de ventanas sobre la que se apoya Tauri: ponerlo en false no logra impedir de forma confiable que macOS active la app. Bien, callejón sin salida, esperable. La conclusión desde ahí fue que la única opción restante era ir más allá de la API soportada de Tauri hacia llamadas Cocoa sin procesar, lo cual pareció demasiado riesgoso para recomendar. Lo que volvió fue todo un informe de “no construir esto” con ese razonamiento, más el premio de consuelo de avisarme antes de que una sesión acapare el foco.
Mi respuesta fue directa: esa es una mala solución, anula el propósito mismo de la automatización. “Que me avisen antes de que me interrumpan” no es una corrección, es solo programar la interrupción para más tarde. Ese cuestionamiento fue lo que forzó una segunda mirada en vez de una defensa de la primera respuesta.
La segunda pasada encontró que la primera estaba equivocada en ambos frentes. tauri::App::set_activation_policy es una API de Tauri real y ya publicada, no un workaround riesgoso de FFI. Solo está documentada en el tipo equivocado (App, no el más usado AppHandle), que probablemente es la razón por la que la primera pasada la pasó por alto. Y la preocupación de “riesgo para la automatización” se disolvió al revisar de verdad cómo el plugin de WebDriver maneja la app: todo sucede evaluando JavaScript dentro del webview y devolviendo resultados por IPC. Nada de eso toca clics o teclas reales a nivel de sistema operativo. Una app que no acapara el Dock ni se autoactiva no tiene forma de interferir con eso, porque ese mecanismo nunca estuvo en el camino desde un principio.
La corrección terminó siendo de una sola línea, protegida detrás de la misma bandera exclusiva de depuración que ya activa el servidor de automatización. Tampoco me quedé solo con el razonamiento: la verificación registró qué app estaba al frente antes de abrir una sesión de WebDriver, abrió una, volvió a chequear inmediatamente después (sin cambios), y después corrió el script e2e completo del flujo de captura contra la misma build para confirmar que la automatización seguía funcionando exactamente igual que antes.
La lección no es realmente sobre la política de activación de Tauri. Es que la primera conclusión de “esto no vale la pena construirlo” se apoyaba en dos suposiciones sin verificar, una encima de la otra: que no existía una API de primera clase, y que construir una sería riesgoso, y ambas eran incorrectas, pero sonaban lo bastante razonables como para que la investigación se detuviera ahí. Rechazar la conclusión de plano, en vez de cuestionar el razonamiento en detalle, fue lo que realmente hizo que la investigación se reabriera.
Lecturas relacionadas
Agregar un menú nativo de macOS a Greenhouse rompió Cmd+Q, y mi harness de pruebas tampoco pudo presionar Cmd+N
set_menu reemplaza el menú por defecto, no lo extiende, y se lleva con él Quit, Hide y todos los atajos de Edit. Después resultó que el endpoint de actions de WebDriver no podía presionar una tecla.
Enseñarle a un gestor de proyectos a reconocer una sesión de Logic Pro
Los archivos .logicx no son archivos, son bundles, y el filtro que ocultaba la basura de .DS_Store también ocultaba cada proyecto de DAW. Más el clic silencioso que no hacía nada y engañó a un bucle de reintento.
Dos notas que nadie vio jamás
Greenhouse guardaba fielmente una nota de captura y una nota de traspaso, y no mostraba ninguna de las dos: una no tenía campo en el tipo de wire, la otra tenía un comando funcional que nadie llamaba.