Enseñarle a un gestor de proyectos a reconocer una sesión de Logic Pro
Greenhouse no sabe nada sobre software de producción musical. Solo gestiona carpetas: capturar una idea, dejarla madurar, moverla entre etapas, guardarla en bóveda si se estanca. Pero buena parte de lo que en realidad vive en esas carpetas son archivos de proyecto de DAW, y hasta este issue, la app trataba una sesión de Logic Pro igual que trataba un .DS_Store perdido: invisible.
El problema
Los archivos .logicx y .band no son archivos. Son directorios que el Finder de macOS presenta como íconos individuales, un truco de empaquetado llamado bundle. El listado de archivos de Greenhouse tenía un filtro de una línea: saltar cualquier cosa que no sea un archivo plano. Ese filtro hacía doble función. Ocultaba correctamente basura como .DS_Store, y también, como efecto secundario, ocultaba cada bundle de proyecto de DAW que a un usuario en verdad le interesara ver.
El arreglo suena chico: reconocer el bundle, ponerle una insignia, agregar un botón “Open in DAW.” La parte interesante fue demostrar que en verdad funcionaba.
Por qué “Open in DAW” fue fácil
La app ya tenía exactamente la plomería que esto necesitaba. “Show in Finder” abre una carpeta a través del mecanismo de manejador por defecto del sistema operativo: pasarle una ruta al SO, dejar que él decida qué app es dueña de eso. Un bundle de DAW es solo otra ruta más. La misma llamada, sin capacidad nueva, sin permiso nuevo. Agregué un comando nuevo, open_item_file, que resuelve un nombre de archivo de vuelta a una ruta real del lado del servidor (nunca confiando en lo que el frontend diga que es una ruta) y reutiliza exactamente la misma llamada de apertura que “Show in Finder” ya usa.
Demostrarlo, no asumirlo
Podría haberme detenido en pruebas unitarias y una prueba de componente simulada, y honestamente esas cubrían la mayor parte de la lógica real: detección de bundles contra un sistema de archivos temporal real, chequeos de contención contra un intento real de path traversal, un render real de Svelte con un clic real de botón. Pero hay un hueco que esas no pueden cerrar: ¿funciona de verdad el cableado de #[tauri::command], de punta a punta, contra una app real corriendo?
Así que construí una sesión de harness con WebDriver contra una bóveda con datos de prueba (SQLite no permite adelantar un timer de maduración, así que escribí la fila del “proyecto activo” directamente en state.db en vez de esperar una semana) y manejé la interfaz real: encontrar la tarjeta del proyecto, hacer clic para entrar, confirmar que las insignias de DAW se renderizan, hacer clic en “Open in DAW” de verdad, confirmar que no hay error.
El primer intento falló. El clic no hizo nada, sin error, sin navegación, solo silencio. Ese silencio resultó ser el bug interesante, y no estaba para nada en mi código nuevo.
El clic que no hizo nada
Le había dicho al script de WebDriver que encontrara toda la tarjeta, el <li>, y le hiciera clic. El nombre de la tarjeta es un botón. La tarjeta en sí no lo es. Hacer clic en el <li> no le hizo clic a… nada, porque nada estaba escuchando. Sin error, porque no había nada que fallara, el clic cayó en un elemento sin handler y la página se quedó ahí sentada.
Ese es un modo de falla más traicionero que un crash. Un crash dice dónde mirar. Un no-op silencioso se ve exactamente igual que “la página todavía no terminó de cargar,” que es precisamente lo que mi bucle de reintento estaba construido para tolerar, así que reintentó pacientemente durante veinte segundos antes de rendirse con un error que apuntaba a algo completamente distinto. El arreglo fue una línea: apuntar a button.name-btn, el elemento realmente clicable, en vez de la fila alrededor.
Resultados
Los bundles .logicx/.band y los archivos .als/.flp/.rpp/.cpr/.bwproject ahora aparecen en el panel de Files con una etiqueta amigable y un botón “Open in DAW.” Hay una advertencia en la pantalla de confirmación de avance si se está por mover una carpeta que contiene un bundle de DAW, el único riesgo real que el spike de investigación original había señalado (mover un proyecto mientras está abierto en el DAW). Y todo esto está respaldado por 222 pruebas de Rust, 135 pruebas de frontend, y una pasada real con WebDriver, captura de pantalla incluida, confirmándolo de punta a punta.
Reflexión
La funcionalidad en sí acá era chica y de bajo riesgo, reutiliza un mecanismo que lleva meses funcionando. El valor de manejarla de verdad no fue demostrar que la funcionalidad funcionaba, las pruebas unitarias ya me daban alta confianza en eso. Fue atrapar un bug en mi prueba, no en mi código, y ese bug habría sido invisible si hubiera confiado en un check verde en vez de mirar qué hacía el clic en realidad. Una prueba que pasa porque está probando lo que no corresponde es peor que ninguna prueba, porque se ve exactamente igual que tener cobertura.
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.
La corrección que me convencí de no hacer
Una recomendación de 'no construir esto' basada en dos suposiciones sin verificar, y la corrección de una sola línea en la política de activación de Tauri que estuvo ahí todo el tiempo.
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.