Buscar/Modificar una aplicación de escritorio grande de medios sin congelar la UI ni romper el deshacer

Modificar una aplicación de escritorio grande de medios sin congelar la UI ni romper el deshacer

Para una aplicación de escritorio grande que maneja archivos y medios: la IA saca el trabajo pesado del hilo de UI, cubre cancelaciones y casos límite, mantiene exacto el deshacer e informa solo de las pruebas que realmente ejecutó.

Para usar estas reglas de IA
## Seguir toda la ruta de llamadas y dar un único dueño a cada estado - Comprende la funcionalidad propietaria y las abstracciones existentes antes de editar, y traza la ruta completa de llamadas a través de la interfaz de usuario, la automatización, deshacer, la persistencia y el trabajo en segundo plano. - Para cambios no triviales, identifica la fuente de verdad, el propietario del estado, el dominio de aislamiento, los invariantes y el comportamiento ante fallos y cancelación antes de escribir código. - Diseña la arquitectura antes que los detalles de implementación, y prefiere un cambio coherente en lugar de parches dispersos en capas no relacionadas. - Mantén un único propietario autoritativo para cada fragmento de estado mutable; el estado derivado se calcula o se mantiene mediante una caché con un contrato de invalidación explícito. - Reutiliza las operaciones de dominio existentes; no añadas una segunda implementación solo porque quien la invoca es una vista previa, una vista, un controlador, una herramienta de automatización o una prueba. - Ubica el código junto a la funcionalidad que lo posee, reserva las utilidades compartidas para la infraestructura usada por varias funcionalidades independientes, y nombra los tipos y archivos de forma que la propiedad quede clara. ## Escribir comentarios breves y quitar el código muerto - Mantén los comentarios al mínimo: escribe uno solo cuando el motivo, el invariante, la restricción de seguridad o la solución alternativa no sean obvios, y limítalo a una sola línea breve. - No narres el código, no repitas los nombres, no describas el parche actual, no dejes rastros de eliminación ni mantengas código comentado. - Prefiere nombres precisos, tipos pequeños y operaciones extraídas antes que comentarios explicativos. - Todo cálculo o regla de negocio tiene una única fuente de verdad; nunca la copies a otro archivo o superficie. - Elimina el código muerto, el estado sin usar, las rutas de compatibilidad obsoletas y los diagnósticos temporales antes de finalizar. - No añadas código de compatibilidad para plataformas, versiones o configuraciones que el proyecto no admite. ## Sacar el trabajo pesado del hilo de UI y revisar el estado tras cada await - Trata el hilo de la interfaz de usuario como un recurso escaso: puede contener el estado de la interfaz y una coordinación ligera, pero no debe realizar operaciones de archivos, decodificación, inferencia de modelos, procesamiento de imágenes, indexación, exportación, llamadas bloqueantes ni transformaciones pesadas. - El aislamiento no es rendimiento: marcar el trabajo como asíncrono o envolverlo en una tarea no lo saca del hilo actual. Sigue al ejecutor real. - Haz explícitos los saltos entre hilos; toma una instantánea de la entrada inmutable mínima antes de abandonar un hilo, devuelve un valor, y aplícalo después de confirmar que sigue siendo vigente. - Después de cada punto de suspensión, asume que el estado compartido cambió: revalida la identidad, la generación, la selección y las precondiciones antes de confirmar un resultado. - Prefiere la concurrencia estructurada; una tarea no estructurada necesita un propietario claro, un identificador almacenado cuando la cancelación importa, y un desmontaje definido. - La cancelación es cooperativa: los bucles largos la comprueban en puntos útiles y nunca confirman resultados parciales u obsoletos. - Limita el trabajo paralelo según el recurso escaso real, elimina duplicados de solicitudes idénticas en curso, adquiere y libera las compuertas dentro de los mismos ámbitos, y reanuda cada continuación o callback exactamente una vez. - Nunca bloquees el hilo de la interfaz de usuario con esperas síncronas, semáforos, bloqueos o sondeo (polling) para trabajo asíncrono. ## Trabajar con archivos fuera del hilo de UI y reemplazarlos de forma atómica - Toda operación del sistema de archivos se ejecuta fuera del hilo de la interfaz de usuario: lecturas, escrituras, comprobaciones de existencia y metadatos, enumeración, copia, movimiento, reemplazo, eliminación y creación de directorios. - Asume que cualquier volumen puede ser lento, extraíble, modificado externamente o estar respaldado por red; un tamaño pequeño o un acceso exitoso previo no hacen seguro un acceso síncrono. - Toma una instantánea de los datos propios del modelo antes de trabajar con archivos, y no mutes el estado observable desde el ejecutor de trabajo de archivos. - Prepara la salida completa fuera del destino activo, arma el reemplazo en el destino y luego instálalo de forma atómica; dirige las escrituras en vivo a través de un único coordinador en lugar de escribir directamente. - Serializa las operaciones que apuntan al mismo archivo o destino, de modo que guardar, importar, generar, crear miniaturas, eliminar, exportar y cerrar nunca compitan entre sí (race condition). - Usa rutas temporales únicas y límpialas tanto en caso de éxito como de fallo o cancelación; nunca elimines ni reemplaces un destino hasta que el reemplazo completo esté listo. - Muestra los fallos de archivo solicitados por el usuario; no los ocultes detrás de errores ignorados, resultados vacíos o respuestas con apariencia de éxito. ## Medir antes de afirmar una mejora y mantener ligeras las rutas frecuentes - Identifica las rutas sensibles al rendimiento, como el renderizado, la reproducción, el manejo de entradas, las actualizaciones de vistas, la importación, la indexación, el guardado, la restauración y la exportación, y trátalas como rutas críticas. - No realices acceso al sistema de archivos, registro (logging), serialización, configuración de modelos o decodificadores, ni otra configuración bloqueante dentro de rutas que se ejecutan por elemento, por fotograma o en interacciones repetidas. - Mide antes de afirmar una mejora: perfila o realiza pruebas de rendimiento sobre la misma carga de trabajo antes y después del cambio. - Corrige la complejidad algorítmica y el trabajo innecesario antes de ajustar detalles de bajo nivel; vigila los recorridos anidados, las ordenaciones repetidas, la mutación por copia dentro de bucles, las asignaciones intermedias y los saltos repetidos entre hilos. - Agrupa y consolida las mutaciones masivas, volcando el estado pendiente antes de instantáneas, deshacer, exportación, cierre y lecturas que deben devolver datos actuales. - Carga e hidrata los datos de forma diferida (lazy), y almacena en caché el trabajo costoso reutilizable solo con una clave explícita, una capacidad, una regla de invalidación y una política para resultados obsoletos. - Reutiliza los objetos costosos cuando su ciclo de vida lo permita e invalídalos ante cambios relevantes de configuración; evita que el estado de alta frecuencia, como el progreso y los medidores, invalide vistas completas; limita la memoria retenida. ## Enumerar los casos límite y de fallo antes de implementar - Trabajar solo en el camino feliz no es suficiente: antes de implementar, enumera los casos límite, de ciclo de vida, de concurrencia y de fallo aplicables, y decide qué capa es responsable de cada uno. - Valida, según corresponda, las entradas vacías, nulas, cero, negativas, máximas, con desbordamiento, no finitas, malformadas, duplicadas, faltantes, obsoletas y no soportadas. - Valida antes de realizar aritmética, indexación o conversión numérica; nunca confíes en un `catch` posterior para absorber una trampa aritmética, y define con precisión el redondeo y la limitación (clamping) en lugar de limitar silenciosamente. - Cubre las operaciones sin efecto (no-op) y repetidas: deben reportar con precisión y no deben generar mutaciones, entradas de historial, trabajo duplicado ni un éxito engañoso. - Considera la cancelación antes de iniciar, durante cada fase y después de que el trabajo termine pero antes de confirmarlo, y considera la finalización obsoleta tras cambios de selección, destino o configuración. - Considera las rutas de desmontaje: cierre, salida, suspensión, reactivación, desactivación, cambios de dispositivo y "guardar como" mientras el trabajo está activo. - Preserva los invariantes en cada ruta de fallo: un éxito parcial debe ser reanudable de forma segura y reportado como tal, o bien revertido. ## Hacer que una acción del usuario se deshaga en un solo paso - Encauza tanto las acciones del usuario como las automatizadas a través de las mismas operaciones de mutación de dominio y el mismo historial compartido. - Una intención coherente del usuario produce una única acción deshacible; no expongas los subpasos internos como entradas de historial independientes a menos que cada uno tenga significado por sí mismo. - Valida los argumentos y las precondiciones antes de abrir un grupo de historial; las operaciones fallidas, canceladas, rechazadas o sin cambios no deben crear entradas vacías. - El trabajo anidado se fusiona en la acción externa sin cerrar los grupos que pertenecen a la plataforma o a otro subsistema. - Deshacer restaura el estado exacto, sin redondeos acumulativos, desviaciones en el estado derivado, relaciones huérfanas ni selecciones obsoletas. - Cuando un cambio afecta al historial, prueba el entrelazado entre ediciones del usuario, ediciones automatizadas, agrupación de eventos de la plataforma y solicitudes concurrentes. ## Diseñar las herramientas de automatización según lo que el usuario quiere lograr - Diseña las herramientas a partir de la intención del usuario, no de las API internas, las operaciones de almacenamiento o los límites de los servicios; define el resultado, los criterios de éxito, las advertencias, el fallo, la cancelación, la idempotencia y la semántica de deshacer antes que el esquema. - Una herramienta realiza una acción coherente; una llamada normalmente completa un flujo de trabajo atómico, comprensible y deshacible. No construyas una herramienta amplia con modos no relacionados, y no obligues a quien la invoca a encadenar herramientas de bajo nivel para un trabajo que el propio sistema puede realizar de forma segura. - Expresa los parámetros en conceptos de dominio orientados al usuario y oculta el diseño del almacenamiento y los detalles de implementación; usa identificadores de entidad estables para la automatización, tratando los índices posicionales y las etiquetas solo como contexto, nunca como identidad duradera. - Trata todo argumento como no confiable: exige tipos exactos, números finitos, límites explícitos e identificadores válidos; resuelve y valida la solicitud completa antes de mutar, y aplica los cambios que afectan a varias entidades de forma atómica. - Reutiliza las mismas operaciones de dominio que usa la interfaz de usuario, y no dupliques la lógica de colocación, vinculación o validación. - Devuelve comprobantes estructurados que describan qué cambió, identificadores estables, el estado sin efecto de forma explícita, advertencias y errores accionables; nunca devuelvas una respuesta con apariencia de éxito cuando el resultado fue ajustado o no se logró, y nunca limites, redirijas o recurras a una alternativa de forma silenciosa a menos que el contrato lo prometa y lo reporte. - Las herramientas de larga duración exponen un trabajo persistente o un resultado final que quien las invoca puede inspeccionar; mantén los contratos orientados a máquina estables y separados del texto localizado; las descripciones explican cuándo, por qué y cómo interactúa una herramienta con las demás. ## Mantener el estado de la UI en su hilo y respetar el comportamiento nativo de la plataforma - Mantén el estado observable de la interfaz de usuario en el hilo de la interfaz, y deja que los resultados de segundo plano crucen el límite como valores inmutables. - Limita la observación a la vista más pequeña que necesita un valor; el estado de alta frecuencia, como el progreso, el hover o la reproducción, no debe invalidar árboles de vistas no relacionados. - No inicies efectos secundarios persistentes desde el cuerpo de renderizado; usa tareas o controladores conscientes del ciclo de vida con cancelación y desmontaje explícitos, y mantén los cuerpos de renderizado rápidos y libres de efectos secundarios. - Preserva el comportamiento nativo de la plataforma para el foco, el teclado, la tecla escape, los menús, la restauración de ventanas, deshacer, el estado de arrastre y la confirmación de cierre. - Los contratos de delegados y controladores de finalización (completion handlers) se completan exactamente una vez en cada ruta de éxito, fallo, cancelación y destino faltante. - No asumas que un callback de un framework o de finalización llega en el hilo de la interfaz de usuario, salvo que la API lo garantice. ## Procesar medios sin bloquear y conservar exactos los valores de tiempo y fotogramas - Usa carga asíncrona de propiedades para los recursos externos, y no llames a accesores síncronos obsoletos que puedan bloquear el hilo que los invoca. - Mantén los valores en su dominio exacto, como enteros precisos de tiempo, fotograma o unidad, durante el mayor tiempo posible, y conviértelos a punto flotante solo en los límites explícitos de la interfaz de usuario o de formatos externos. - Define y preserva la semántica de origen, como el tiempo, la escala, las transformaciones, el color, el canal alfa, la disposición de canales y los metadatos, a menos que la funcionalidad los modifique explícitamente. - Reutiliza lectores, contextos de renderizado y pipelines cuando su ciclo de vida lo permita, y no los reconstruyas durante una interacción continua a menos que la invalidación lo requiera. - Limita los decodificadores, lectores, exportaciones, inferencias y extracciones concurrentes, y propaga la cancelación a través de cada bucle de decodificación, renderizado y exportación, comprobándola entre fragmentos cuando un paso subyacente no se pueda cancelar. - Realiza pruebas con pistas faltantes, contenedores inusuales, duración cero o indefinida, entradas rotadas o con canal alfa, tasas no estándar y cancelación. ## Usar tokens de diseño en lugar de valores de estilo fijos en el código - Da estilo a todo a través del sistema compartido de tokens de diseño; nunca codifiques directamente (hardcode) el espaciado, los tamaños, los grosores de fuente, los radios, los anchos de borde, la opacidad, los colores, las sombras o las duraciones de animación. - Si no existe un token necesario, agrégalo primero al sistema de tokens en lugar de codificar un valor directamente. - La búsqueda de recursos debe funcionar tanto en la aplicación empaquetada como en las ejecuciones locales y las pruebas; usa la abstracción compartida de búsqueda de recursos en lugar de sondeos específicos de cada funcionalidad, y trata cada estructura de paquete (bundle) como una configuración distinta que debe verificarse. - No uses indicadores exclusivos de depuración para seleccionar una ruta de recursos o un comportamiento fundamentalmente distinto, a menos que la diferencia sea intencional y esté probada. - Al añadir un recurso empaquetado, actualiza el manifiesto del paquete, los scripts de compilación y las pruebas, verifica la estructura final empaquetada, y mantén el código opcional controlado por indicadores (feature flags) compilable tanto con el indicador activado como desactivado. ## Dar a cada operación un final visible y registros útiles - Toda operación iniciada por el usuario o por la automatización debe alcanzar un estado observable de éxito, fallo, rechazo o cancelación. - No ignores un error en un límite de interacción cuando el fallo cambia el resultado; conviértelo en estado de la interfaz de usuario, en un error de herramienta, en un fallo de trabajo o en un registro accionable. - El trabajo de tipo "dispara y olvida" (fire-and-forget) solo se permite cuando es explícitamente de mejor esfuerzo; no debe ser responsable de una persistencia obligatoria, y sus fallos deben poder ignorarse de forma segura o quedar registrados. - Registra los fallos junto con la operación, identificadores estables y la fase del ciclo de vida; nunca escribas secretos, credenciales ni entradas del usuario sin procesar en los registros. - Conserva suficiente información de diagnóstico para distinguir entre cancelación, fallo de validación, recurso no disponible, fallo del framework y violación de un invariante interno. - Usa las aserciones (assertions) únicamente para invariantes del programador, no para entradas de usuario recuperables, archivos externos, condiciones de carrera del ciclo de vida o solicitudes de automatización. ## Escribir pruebas pequeñas y deterministas que puedan ejecutarse en paralelo - Las pruebas unitarias son concisas y se centran en el comportamiento: una prueba establece un solo invariante o una sola regresión, con únicamente la configuración necesaria para dejarlo claro, usando nombres descriptivos y sin comentarios que narren los pasos. - Usa pruebas parametrizadas para las matrices de casos límite y constructores de fixtures compartidos en lugar de duplicar la configuración entre suites. - Prueba a través de la interfaz estable más pequeña que demuestre el comportamiento, y evita acoplarte a detalles privados o a recuentos de llamadas incidentales. - Toda corrección de errores recibe una prueba de regresión que falla ante el defecto original cuando sea posible, y cubre las rutas negativas: validación, operaciones sin efecto, fallo parcial, cancelación, finalización obsoleta y limpieza. - Las pruebas de concurrencia se coordinan de forma determinista mediante compuertas, continuaciones o puntos de enganche (hooks) inyectados, y no dependen de esperas (sleeps), de la suerte del tiempo o del orden de planificación. - Las pruebas se ejecutan en paralelo: no compartas estado global mutable, nombres temporales fijos, puertos o directorios; usa directorios temporales únicos y límpialos después, y mantén las credenciales reales, los servicios externos y los archivos reales de usuario fuera de las pruebas unitarias. ## Revisar primero la corrección e informar exactamente qué pruebas se ejecutaron - Revisa primero la corrección y después el estilo: rastrea el comportamiento modificado a través de quienes lo invocan, el estado compartido, el trabajo en segundo plano, la persistencia, deshacer, la interfaz de usuario y las superficies de automatización. - Prioriza los hallazgos que causan pérdida de datos, corrupción, caídas, bloqueos, condiciones de carrera, exposición de seguridad, resultados incorrectos, un deshacer roto o un éxito engañoso; después, el rendimiento, la mantenibilidad y el pulido. - Busca bloqueos del hilo de la interfaz de usuario, errores de concurrencia como estado confirmado tras una suspensión sin revalidación, tareas sin propietario, cancelación faltante, ramificación (fan-out) sin límite, compuertas desequilibradas y callbacks reanudados dos veces, además de lógica de dominio duplicada, cachés inseguras y aritmética de límites sin validar; limita la revisión al cambio y a lo que este afecta directamente. - Verifica las afirmaciones: ejecuta la compilación y las pruebas apropiadas para el cambio, ejercita las rutas de cancelación y de ciclo de vida en los cambios de concurrencia, y mide los cambios de rendimiento bajo una carga de trabajo representativa. - Reporta exactamente qué se ejecutó y qué no; no afirmes una verificación completa de compilación, pruebas o tiempo de ejecución cuando solo se completó un subconjunto. - Mantén los commits enfocados, con un prefijo de categoría preciso y acotado, sin limpieza no relacionada, y describe un cambio por su resumen, su enfoque, las decisiones clave de diseño y la evidencia de pruebas, en lugar de la secuencia de ediciones realizadas.