Atualização do autor (26/08/2026): sou Danila Pryadko e desenvolvo o Tabwell. A geração de recuperação 1.1 já está pública. A versão 1.1.0 introduziu bundles com várias janelas, o journal durável, o Gentle Restore e os fluxos protegidos de Salvar e fechar e Substituir atual; a 1.1.1 é o hardening de correção e usabilidade guiado por testes externos. Não atribuo toda a arquitetura à correção 1.1.1.
O problema do texto original — “9 de 10 abas foram restauradas; repetir tudo seria outro bug” — fica mais interessante quando o snapshot tem várias janelas. O Chrome não oferece uma transação única entre janelas. Portanto, chamar a restauração de “atômica” esconderia justamente o estado que precisa ser tratado: uma janela pode ter sido reconstruída, outra pode falhar ao receber metadados de grupo e uma terceira ainda nem ter começado.
O contrato que passei a usar é de falha segura, não de atomicidade global:
- validar o bundle inteiro antes da primeira mutação;
- registrar no journal a operação e o frontier que ela já alcançou;
- preservar, por janela, quais alvos foram criados pela operação;
- confirmar o resultado somente depois de ordem, abas fixadas, aba ativa e metadados dos grupos passarem pelas verificações previstas;
- em uma falha terminal, remover apenas estruturas cuja propriedade ainda possa ser revalidada.
bundle validado
└─ journal durável (operationId + frontier)
├─ Janela A — confirmada
├─ Janela B — falha parcial
│ └─ reler grupo e abas
│ ├─ ownership confirmado → remover só a casca criada
│ └─ ownership ambíguo → preservar e relatar parcial
└─ Janela C — não iniciada
Mapa textual conceitual: uma restauração com várias janelas pode terminar em sucesso, resultado parcial ou rollback limitado aos alvos que ainda pertencem à operação.
O quinto item é a fronteira destrutiva. Um ID de grupo retornado pelo Chrome é evidência de que um grupo existiu; não é uma autorização permanente para apagá-lo. Entre a criação e o rollback, o usuário pode mover uma aba, renomear o grupo ou reaproveitar aquela janela. Antes de remover uma “casca” de grupo depois de uma atualização de metadados esgotada, a operação precisa conferir de novo o alvo, sua relação com a operação e as abas que ainda estão dentro dele. Se a propriedade ficou ambígua, o resultado correto é deixar o estado visível e reportar a falha — não adivinhar e apagar.
Em pseudocódigo, a diferença é esta:
const shell = await readGroup(targetGroupId);
if (!shell || !stillOwnedBy(operation, shell)) {
return { kind: 'partial', cleanup: 'skipped-ambiguous-owner' };
}
await removeOnlyOwnedShell(shell.id);
return { kind: 'partial', cleanup: 'confirmed' };
Isso não transforma uma sequência de chamadas da API do Chrome em transação. A vantagem é mais estreita e verificável: uma falha tardia não autoriza automaticamente a extensão a desfazer trabalho que talvez já não seja só dela.
O journal também muda o significado de “tentar de novo”. Depois de uma interrupção do service worker ou do perfil inteiro, a pergunta não é “qual botão o usuário apertou por último?”, mas “qual operação durável existe, qual frontier foi confirmado e quais efeitos podem ser retomados sem duplicação?”. Uma nova tentativa cega abriria janelas e abas paralelas. Retomar a mesma operação permite continuar do checkpoint; se o estado externo não satisfaz mais as invariantes, o fluxo deve parar com um resultado parcial explícito.
Na 1.1.1, um caso concreto desse princípio foi o rollback de grupos: após esgotar a atualização de metadados, a limpeza passou a remover somente a casca exatamente revalidada como pertencente à operação. Outro ajuste evitou que o Substituir atual abortasse por um pendingUrl transitório com a mesma URL, mantendo o bloqueio quando existe divergência real. São correções do hardening 1.1.1 sobre a arquitetura lançada na 1.1.0.
O teste útil não é apenas “todas as abas abriram”. Eu separaria pelo menos estes casos:
- falha antes de qualquer janela nova;
- primeira janela concluída e segunda interrompida;
- grupo criado, mas atualização de nome/cor esgotada;
- usuário altera o grupo antes do rollback;
- service worker reinicia entre criação e confirmação;
- mesmo
pendingUrltransitório versus URL realmente diferente; - retomada do mesmo journal sem duplicar o que já foi confirmado.
O resultado pode continuar sendo 9 de 10, mas agora há respostas diferentes para três perguntas: o que foi processado, o que ficou confirmado e o que ainda pertence à operação e pode ser revertido com segurança. Misturar essas respostas em um único booleano é que tornaria a recuperação destrutiva.
Usei IA para apoiar a estrutura e a revisão em português. Conferi as afirmações técnicas no changelog, no código e nos testes congelados da versão pública 1.1.1. Esta atualização é um registro de implementação do próprio autor, não uma avaliação independente nem uma promessa de recuperação sem perda.