Buscar/Alterar uma aplicação de mídia grande para desktop sem travar a UI nem quebrar o desfazer

Alterar uma aplicação de mídia grande para desktop sem travar a UI nem quebrar o desfazer

Para uma aplicação grande para desktop que lida com arquivos e mídia: a IA tira o trabalho pesado da thread de UI, cobre cancelamentos e casos extremos, mantém o desfazer exato e informa só os testes que realmente rodou.

Para usar estas regras de IA
## Seguir todo o caminho de chamadas e dar um único dono a cada estado - Entenda a funcionalidade responsável e as abstrações existentes antes de editar, e rastreie todo o caminho de chamadas pela UI, automação, desfazer, persistência e trabalho em segundo plano. - Para mudanças não triviais, identifique a fonte da verdade, o dono do estado, o domínio de isolamento, os invariantes e o comportamento de falha e cancelamento antes de escrever código. - Desenhe a arquitetura antes dos detalhes de implementação, e prefira uma mudança coerente a remendos espalhados por camadas não relacionadas. - Mantenha um único dono autoritativo para cada parte do estado mutável; o estado derivado é calculado ou mantido por um cache com um contrato de invalidação explícito. - Reutilize as operações de domínio existentes; não adicione uma segunda implementação só porque quem chama é uma pré-visualização, uma view, um controller, uma ferramenta de automação ou um teste. - Coloque o código junto da funcionalidade que é dona dele, reserve utilitários compartilhados para infraestrutura usada por várias funcionalidades independentes, e nomeie tipos e arquivos de forma que a propriedade fique clara. ## Manter comentários curtos e remover código morto - Mantenha os comentários no mínimo: escreva um apenas quando o motivo, o invariante, a restrição de segurança ou a solução alternativa não forem óbvios, e limite-o a uma linha curta. - Não narre o código, não repita nomes em comentários, não descreva o patch atual, não deixe rastros de remoção nem mantenha código comentado. - Prefira nomes precisos, tipos pequenos e operações extraídas em vez de comentários explicativos. - Um cálculo ou uma regra de negócio tem uma única fonte da verdade; nunca a copie para outro arquivo ou superfície. - Remova código morto, estado não utilizado, caminhos de compatibilidade obsoletos e diagnósticos temporários antes de finalizar. - Não adicione código de compatibilidade para plataformas, versões ou configurações que o projeto não suporta. ## Tirar o trabalho pesado da thread de UI e reconferir o estado após cada await - Trate a thread de UI como um recurso escasso: ela pode manter estado de UI e coordenação leve, mas não deve realizar operações de arquivo, decodificação, inferência de modelo, processamento de imagem, indexação, exportação, chamadas bloqueantes ou transformações grandes. - Isolamento não é desempenho: marcar um trabalho como assíncrono ou envolvê-lo em uma task não o tira da thread atual. Siga o executor real. - Torne as trocas de thread explícitas; capture o mínimo de entrada imutável necessária antes de sair de uma thread, retorne um valor e só então aplique-o depois de confirmar que ainda está atualizado. - Depois de todo ponto de suspensão, assuma que o estado compartilhado mudou: revalide identidade, geração, seleção e pré-condições antes de confirmar um resultado. - Prefira concorrência estruturada; uma task não estruturada precisa de um dono claro, um handle armazenado quando o cancelamento importa, e um encerramento bem definido. - O cancelamento é cooperativo: laços longos devem verificá-lo em pontos úteis e nunca confirmar resultados parciais ou desatualizados. - Limite o trabalho paralelo pelo recurso escasso real, elimine requisições idênticas já em andamento, adquira e libere gates em escopos correspondentes, e retome cada continuation ou callback exatamente uma vez. - Nunca bloqueie a thread de UI com esperas síncronas, semáforos, locks ou polling para trabalho assíncrono. ## Fazer o trabalho com arquivos fora da thread de UI e substituí-los de forma atômica - Toda operação de sistema de arquivos roda fora da thread de UI: leituras, escritas, verificações de existência e metadados, enumeração, cópia, movimentação, substituição, exclusão e criação de diretórios. - Assuma que qualquer volume pode ser lento, removível, modificado externamente ou baseado em rede; um tamanho pequeno ou um acesso anterior bem-sucedido não torna o acesso síncrono seguro. - Capture uma cópia dos dados do modelo antes do trabalho com arquivos, e não altere estado observável a partir do executor de trabalho com arquivos. - Prepare a saída completa fora do destino ativo, monte a substituição no destino e então instale-a atomicamente; direcione as escritas ativas por um único coordenador em vez de escrever diretamente. - Serialize operações que atingem o mesmo arquivo ou destino para que salvar, importar, gerar, criar miniaturas, excluir, exportar e fechar nunca entrem em disputa de concorrência (race condition). - Use caminhos temporários únicos e limpe-os em caso de sucesso, falha e cancelamento; nunca exclua ou substitua um destino até que a substituição completa esteja pronta. - Exponha as falhas de arquivo solicitadas pelo usuário; não as esconda atrás de erros ignorados, resultados vazios ou respostas que aparentam sucesso. ## Medir antes de afirmar uma melhora e manter leves os caminhos frequentes - Identifique caminhos sensíveis a desempenho, como renderização, reprodução, tratamento de entrada, atualizações de view, importação, indexação, salvamento, restauração e exportação, e trate-os como críticos (hot paths). - Não realize acesso ao sistema de arquivos, logging, serialização, configuração de modelo ou decodificador, nem outra configuração bloqueante dentro de caminhos executados por item, por frame ou por interação repetida. - Meça antes de afirmar uma melhoria: faça profiling ou benchmark da mesma carga de trabalho antes e depois da mudança. - Corrija a complexidade algorítmica e o trabalho desnecessário antes de ajustes de baixo nível; fique atento a varreduras aninhadas, ordenações repetidas, mutação por cópia dentro de laços, alocações intermediárias e trocas repetidas de thread. - Agrupe e combine mutações em lote, descarregando o estado pendente antes de snapshots, desfazer, exportação, fechamento e leituras que precisam retornar dados atuais. - Carregue e hidrate os dados de forma preguiçosa (lazy), e armazene em cache trabalho caro e reutilizável apenas com uma chave explícita, capacidade, regra de invalidação e política para resultados desatualizados. - Reutilize objetos caros sempre que o ciclo de vida permitir e invalide-os em mudanças relevantes de configuração; evite que estados de alta frequência, como progresso e medidores, invalidem views inteiras; limite a memória retida. ## Listar casos extremos e de falha antes de implementar - Trabalhar apenas no caminho feliz (happy path) não é suficiente: antes de implementar, liste os casos aplicáveis de limite, ciclo de vida, concorrência e falha, e decida qual camada é responsável por cada um. - Valide entradas vazias, nulas, zero, negativas, máximas, com overflow, não finitas, malformadas, duplicadas, ausentes, desatualizadas e não suportadas, conforme aplicável. - Valide antes de operações aritméticas, indexação ou conversão numérica; nunca conte com um catch posterior para absorver uma armadilha aritmética, e defina arredondamento e limitação (clamping) exatos em vez de limitar silenciosamente. - Cubra operações que não fazem nada (no-op) e operações repetidas: elas devem reportar com precisão e não devem criar mutações, entradas de histórico, trabalho duplicado ou sucesso enganoso. - Considere o cancelamento antes do início, durante cada fase e após o trabalho terminar mas antes da confirmação, e considere conclusões desatualizadas após mudanças de seleção, alvo ou configuração. - Considere os caminhos de encerramento: fechar, sair, suspender, despertar, desativação, mudanças de dispositivo e salvar como enquanto o trabalho está em andamento. - Preserve os invariantes em todo caminho de falha: um sucesso parcial deve ser retomável com segurança e reportado como tal, ou revertido. ## Fazer cada ação do usuário ser desfeita em um único passo - Direcione as ações do usuário e as ações automatizadas pelas mesmas operações de mutação de domínio e pelo mesmo histórico compartilhado. - Uma intenção coerente do usuário produz uma única ação desfazível; não exponha subetapas internas como entradas separadas no histórico, a menos que cada uma tenha significado independente. - Valide argumentos e pré-condições antes de abrir um grupo de histórico; operações que falharam, foram canceladas, recusadas ou que não mudaram nada não devem criar entradas vazias. - O trabalho aninhado deve se fundir na ação externa sem fechar grupos pertencentes à plataforma ou a outro subsistema. - Desfazer deve restaurar o estado exato, sem arredondamento cumulativo, desvio de estado derivado, relacionamentos órfãos ou seleção desatualizada. - Quando uma mudança afeta o histórico, teste o intercalamento entre edições do usuário, edições automatizadas, agrupamento de eventos da plataforma e requisições concorrentes. ## Projetar ferramentas de automação a partir do que o usuário quer fazer - Projete ferramentas a partir da intenção do usuário, não a partir de APIs internas, operações de armazenamento ou limites de serviço; defina o resultado, os critérios de sucesso, os avisos, a falha, o cancelamento, a idempotência e a semântica de desfazer antes do schema. - Uma ferramenta executa uma ação coerente; uma chamada normalmente completa um fluxo de trabalho atômico, compreensível e desfazível. Não crie uma ferramenta genérica com modos não relacionados, e não force quem a chama a encadear ferramentas de baixo nível para um trabalho que o próprio sistema pode fazer com segurança. - Expresse os parâmetros em conceitos de domínio voltados ao usuário e oculte o layout de armazenamento e os detalhes de implementação; use identificadores de entidade estáveis para automação, tratando índices posicionais e rótulos apenas como contexto, não como identidade durável. - Trate todo argumento como não confiável: exija tipos exatos, números finitos, limites explícitos e identificadores válidos; resolva e valide a requisição completa antes de mutar, e aplique mudanças em múltiplas entidades de forma atômica. - Reutilize as mesmas operações de domínio usadas pela UI, e não duplique a lógica de posicionamento, vinculação ou validação. - Retorne recibos estruturados descrevendo o que mudou, identificadores estáveis, estado explícito de no-op, avisos e erros acionáveis; nunca retorne uma resposta que aparente sucesso quando o resultado foi ajustado ou não foi alcançado, e nunca limite, redirecione ou use um fallback silenciosamente, a menos que o contrato prometa e reporte isso. - Ferramentas de longa duração devem expor um job durável ou um resultado final que quem chama possa inspecionar; mantenha os contratos voltados à máquina estáveis e separados do texto localizado; as descrições devem explicar quando, por que e como uma ferramenta interage com as outras. ## Manter o estado da UI na thread de UI e preservar o comportamento nativo da plataforma - Mantenha o estado observável da UI na thread de UI e deixe que resultados de segundo plano cruzem a fronteira como valores imutáveis. - Restrinja a observação à menor view que precisa de um valor; estados de alta frequência, como progresso, hover e reprodução, não devem invalidar árvores de views não relacionadas. - Não inicie efeitos colaterais persistentes a partir do corpo de renderização; use tasks ou controllers cientes do ciclo de vida, com cancelamento e encerramento explícitos, e mantenha os corpos de renderização rápidos e livres de efeitos colaterais. - Preserve o comportamento nativo da plataforma para foco, teclado, tecla escape, menus, restauração de janela, desfazer, estado de arrastar (drag) e confirmação de fechamento. - Contratos de delegate e de completion handler devem se completar exatamente uma vez em todo caminho de sucesso, falha, cancelamento e alvo ausente. - Não assuma que um callback de framework ou de conclusão chega na thread de UI, a menos que a API garanta isso. ## Processar mídia sem bloquear e manter exatos os valores de tempo e quadro - Use carregamento assíncrono de propriedades para assets externos, e não chame acessores síncronos obsoletos que possam bloquear a thread de quem chama. - Mantenha os valores em seu domínio exato, como inteiros precisos de tempo, frame ou unidade, pelo maior tempo possível, e converta para ponto flutuante apenas em fronteiras explícitas de UI ou de formato externo. - Defina e preserve a semântica de origem, como timing, escala, transformações, cor, alfa, layout de canais e metadados, a menos que a funcionalidade os altere explicitamente. - Reutilize readers, contextos de renderização e pipelines sempre que o ciclo de vida permitir, e não os reconstrua durante interação contínua, a menos que a invalidação exija isso. - Limite decodificadores, readers, exportações, inferências e extrações concorrentes, e propague o cancelamento por todo laço de decodificação, renderização e exportação, verificando entre os blocos (chunks) quando uma etapa subjacente não pode ser cancelada. - Teste com faixas ausentes, contêineres incomuns, duração zero ou indefinida, entradas rotacionadas ou com alfa, taxas fora do padrão e cancelamento. ## Usar tokens de design em vez de valores de estilo fixos no código - Estilize tudo por meio do sistema compartilhado de design tokens; nunca fixe diretamente no código (hardcode) espaçamentos, tamanhos, pesos de fonte, raios, larguras de borda, opacidade, cores, sombras ou durações de animação. - Se um token necessário não existir, adicione-o ao sistema de tokens primeiro, em vez de fixar um valor diretamente no código. - A busca de recursos deve funcionar na aplicação empacotada, em execuções locais e em testes; use a abstração compartilhada de busca de recursos em vez de sondagens específicas de cada funcionalidade, e trate cada layout de bundle como uma configuração distinta a ser verificada. - Não use flags exclusivas de debug para selecionar um caminho de recurso ou comportamento fundamentalmente diferente, a menos que a diferença seja intencional e testada. - Ao adicionar um recurso empacotado, atualize o manifesto do pacote, os scripts de build e os testes, verifique o layout final empacotado, e mantenha o código opcional controlado por feature flag compilável tanto com quanto sem a flag. ## Dar a cada operação um fim visível e logs úteis - Toda operação iniciada pelo usuário ou por automação deve chegar a um estado observável de sucesso, falha, recusa ou cancelamento. - Não ignore um erro em uma fronteira de interação quando a falha muda o resultado; converta-o em estado de UI, erro de ferramenta, falha de job ou log acionável. - Trabalho do tipo "fire-and-forget" só é permitido quando é explicitamente de melhor esforço; ele não deve ser dono de persistência obrigatória, e suas falhas devem poder ser ignoradas com segurança ou registradas. - Registre falhas com a operação, identificadores estáveis e a fase do ciclo de vida; nunca grave segredos, credenciais ou entradas brutas do usuário em logs. - Preserve diagnósticos suficientes para distinguir cancelamento, falha de validação, recurso indisponível, falha de framework e violação de invariante interno. - Use assertions apenas para invariantes de programação, não para entradas de usuário recuperáveis, arquivos externos, disputas de ciclo de vida (races) ou requisições de automação. ## Escrever testes pequenos e determinísticos que rodem em paralelo - Testes unitários devem ser concisos e focados em comportamento: um teste estabelece um invariante ou uma regressão com apenas a configuração necessária para deixá-lo claro, usando nomes descritivos e sem comentários que narrem os passos. - Use testes parametrizados para matrizes de casos-limite e builders de fixtures compartilhados em vez de duplicar a configuração entre suítes. - Teste pela menor costura (seam) estável que comprove o comportamento, e evite acoplamento a detalhes privados ou a contagens incidentais de chamadas. - Toda correção de bug ganha um teste de regressão que falha para o defeito original quando é viável, e cobre caminhos negativos: validação, no-op, falha parcial, cancelamento, conclusão desatualizada e limpeza. - Testes de concorrência devem se coordenar de forma determinística com gates, continuations ou hooks injetados, e não devem depender de sleeps, sorte de timing ou ordem de agendamento. - Os testes rodam em paralelo: não compartilhe estado global mutável, nomes temporários fixos, portas ou diretórios; use diretórios temporários únicos e limpe-os depois, e mantenha credenciais reais, serviços externos e arquivos reais de usuário fora dos testes unitários. ## Revisar a correção primeiro e informar exatamente quais testes rodaram - Revise a correção antes do estilo: rastreie o comportamento alterado por quem chama, o estado compartilhado, o trabalho em segundo plano, a persistência, o desfazer, a UI e as superfícies de automação. - Priorize achados que causam perda de dados, corrupção, crashes, travamentos, race conditions, exposição de segurança, resultados incorretos, desfazer quebrado ou sucesso enganoso, e só depois desempenho, manutenibilidade e acabamento. - Procure bloqueio da thread de UI, erros de concorrência como estado confirmado após uma suspensão sem revalidação, tasks sem dono, cancelamento ausente, fan-out sem limite, gates desbalanceados e callbacks retomados duas vezes, além de lógica de domínio duplicada, caches inseguros e aritmética de limite não validada; restrinja a revisão à mudança e ao que ela afeta diretamente. - Verifique as afirmações: rode o build e os testes apropriados para a mudança, exercite os caminhos de cancelamento e de ciclo de vida para mudanças de concorrência, e meça mudanças de desempenho sob uma carga de trabalho representativa. - Reporte exatamente o que foi e o que não foi executado; não afirme verificação completa de build, teste ou runtime quando apenas um subconjunto foi concluído. - Mantenha os commits focados, com um prefixo de categoria estreito e preciso e sem limpezas não relacionadas, e descreva uma mudança pelo seu resumo, abordagem, principais decisões de design e evidências de teste, em vez da sequência de edições feitas.