A pasta que ficou parada
O Greenhouse, meu gerenciador de projetos criativos, tem uma regra embutida em quase toda parte dele: a pasta de um projeto mora onde o status e o estágio dele mandam. Projeto ativo no estágio Explore? A pasta fica em 20-explore/. Arquivou? A pasta vai para 90-vault/. Toda tela que abre uma pasta ou lista seus arquivos simplesmente calcula o caminho a partir da linha do banco de dados. Ela nunca precisa perguntar ao sistema de arquivos “espera, onde eu realmente coloquei isso?”
Encontrei o único lugar onde essa suposição era falsa durante uma segunda revisão de código completa do app (faço essas revisões periodicamente, tratando o código como se merecesse um par de olhos de fora mesmo quando não há um). O bug estava em “adotar e importar”, a feature que deixa você apontar o Greenhouse para uma pasta que já existia no disco e trazê-la para o pipeline em vez de começar do zero.
O atalho que fazia sentido na época
Quando construí o adotar/importar, tomei uma decisão deliberada: importar no lugar. Gravar a linha do banco, gerar um arquivo project.md e deixar a pasta exatamente onde o navegador de arquivos do sistema operacional do usuário já a tinha. Mover arquivos parecia mais risco do que a feature precisava para uma primeira versão, e o comentário no código dizia isso explicitamente:
/// v1 is adopt-in-place - the folder is recorded and mirrored where it sits;
/// it is not physically moved into the stage directory.
Isso parece um corte de escopo razoável. Não era. Toda outra mudança de status no app (promover uma ideia, avançar um estágio, arquivar um projeto) move fisicamente a pasta como parte da transição. Importar era a única porta de entrada para “projeto ativo” que não fazia isso. Então, no momento em que você importava uma pasta, a localização real dela no disco e a localização que todo resolvedor de caminho calculava a partir do banco de dados divergiam, permanentemente.
O que isso realmente quebrava
Depois de rastrear o problema: o visualizador de arquivos dentro do app dava erro em todo item importado, porque ele procurava na pasta de estágio calculada e os arquivos ainda estavam sentados na raiz do cofre. “Mostrar no Finder” abria uma pasta que ainda não existia. Na primeira vez que você tocava ou avançava um projeto importado, o Greenhouse diligentemente criava a pasta de estágio que esperava encontrar, escrevia um project.md novinho dentro dela, e deixava a pasta com seus arquivos de verdade encalhada na raiz para sempre, uma pasta fantasma guardando um arquivo ao lado da pasta de verdade guardando tudo que importava. Arquivar o item virava o status dele para Vaulted no banco de dados enquanto os arquivos não se moviam um centímetro.
Nada disso apareceu nos testes existentes, porque os testes de importação paravam em “a linha do banco parece certa e o project.md foi escrito.” Nada encadeava a importação com as operações que vêm depois dela: visualizar um arquivo, avançar um estágio, arquivar a coisa. A verificação de ponta a ponta que confirmava que a importação funcionava nunca ia além de ver o projeto importado aparecer como um card.
A correção já estava no código
A parte boa de um código com um invariante consistente é que corrigir uma violação dele significa copiar um padrão que já existe em outros três lugares. promote_idea, a função que transforma uma ideia capturada em um projeto ativo, faz exatamente o que a importação precisava fazer: calcular o destino sob o diretório de estágio, criar o pai se necessário, renomear a pasta se ela ainda não estiver lá, e então escrever o arquivo espelho no novo local.
let dest = stage_dir(root, stage).join(&candidate.name);
if candidate.path != dest {
if let Some(parent) = dest.parent() {
std::fs::create_dir_all(parent)?;
}
std::fs::rename(&candidate.path, &dest)?;
}
Mover antes de gravar no banco, não depois. Se o rename falhar, a pasta continua intacta onde começou; se eu tivesse inserido a linha do banco primeiro e o rename falhasse depois, teria reproduzido exatamente o bug que estava corrigindo: uma linha do banco apontando para um local que o arquivo nunca alcançou.
Um efeito colateral que gostei: a correção também resolveu de graça um problema menor e relacionado. Re-escanear pastas importáveis costumava precisar de uma checagem no banco para evitar oferecer para sempre uma pasta já importada, já que adotar-no-lugar significava que a pasta simplesmente continuava sentada ali no disco parecendo importável. Agora que a importação de fato move a pasta, o escaneamento bruto do sistema de arquivos deixa de vê-la completamente. A checagem no banco passa de essencial para uma linha de defesa extra, de graça.
Escrevendo os testes que teriam pego isso
O resultado útil de uma revisão como essa não é só a correção, é a lista de testes que faltavam, porque cada sintoma mapeia direto para um teste. Acabei com cinco: importar e checar se o caminho de “mostrar no Finder” realmente existe, importar e confirmar que o visualizador lista o arquivo gerado, importar e avançar e checar se a pasta seguiu o estágio (nada encalhado no local antigo), importar e tocar e confirmar que nenhuma pasta fantasma aparece, e importar e arquivar e checar se os arquivos realmente foram parar no diretório do cofre. Cada um desses é um teste que teria falhado no código antigo e passa agora.
Também rodei a correção por um build real em execução, não só pela suíte de testes do Rust. O Greenhouse tem uma configuração pequena de WebDriver exatamente para isso: ela abre o app Tauri de verdade contra um cofre descartável e clica através dele como uma pessoa faria. Estendi o script de importação existente para soltar um arquivo WAV de verdade na pasta antes de importá-la, depois cliquei através dela para pré-visualizá-la e avançá-la um estágio, checando se o elemento de áudio realmente resolvia e tocava pela URL do protocolo de asset, e se o arquivo continuava ao lado do project.md depois da mudança de estágio. Ver um clique real abrir um player de áudio real buscando um arquivo que tinha se movido fisicamente entre dois diretórios foi mais convincente do que qualquer assertion num teste unitário.
O que eu levo disso
A lição não é “testar mais.” É perceber quando um sistema se apoia tanto num invariante que nada o checa diretamente, porque todo caminho de código existente por acaso o preserva por construção. É exatamente essa a condição sob a qual um novo caminho de código pode violá-lo silenciosamente. Se quatro funções num arquivo realocam uma pasta como efeito colateral e você está prestes a adicionar uma quinta que não faz isso, o que você está adicionando é um caso especial que precisa da própria justificativa, por mais que pareça uma feature menor. Nesse caso não havia justificativa nenhuma, então a correção foi parar de ser especial.
Leitura relacionada
Ensinando um agente a testar cliques no meu app Tauri
O macOS não tem um WebDriver oficial do Tauri, um plugin da comunidade preenche essa lacuna, e a unificação de features do Cargo quase compilou o servidor de teste inteiro nos builds de release.
O scan que se ofereceu para importar a si mesmo
Um scanner de importação que encontrou a própria estrutura interna do app, e depois voltou a oferecer uma pasta que tinha acabado de importar, duas versões da mesma conversa que faltava entre camadas.
Ensinando o Greenhouse a pré-visualizar áudio do vault dentro do app
Uma investigação no protocolo de assets do Tauri, um acesso em runtime para o vault do usuário, um alargamento estreito da CSP, e um bug de autoplay pego primeiro pelos testes de a11y.
Você também pode achar útil
eSIM Airalo
eSIM de dados local para viagens - sem necessidade de trocar um SIM físico.
Este é meu link de indicação da Airalo. Você recebe um desconto no seu primeiro eSIM e eu ganho crédito da Airalo para o meu.
Saiba maisNordPass
Gerenciador de senhas da equipe por trás da NordVPN, com um plano gratuito.
Como afiliado da NordPass, ganho com compras qualificadas.
Saiba maisRackNerd VPS
Hospedagem VPS econômica para serviços leves que funcionam continuamente.
Como afiliado da RackNerd, ganho com compras qualificadas.
Saiba mais