0040 — Assinou, fecha o dossiê: alterar arquivo vira exceção nomeada e visível¶
| Status | CONCLUÍDA |
| Branch | spec/0040-dossie-da-garantia-fechado |
| Criada em | 2026-08-19 |
| Apps afetados | api · web · e2e |
| Revisão do problema | aprovada na rodada 4 |
| Revisão da experiência | aprovada na rodada 4 |
| Revisão do detalhamento | fechada na 6ª rodada, por decisão do usuário |
| Revisão da implementação | api aprovada na 4ª · branch aprovada na 1ª |
Status:
RASCUNHO→APROVADA→EM IMPLEMENTAÇÃO→CONCLUÍDA
A spec tem três partes, escritas em ordem, e quatro portões — cada um revisado por um subagente de contexto limpo, em até 3 rodadas. A máquina de revisão é do plugin superpowers; a régua é a rubrica correspondente em .claude/rubrics/. Achados vêm classificados: qualquer Critical reprova, Important em aberto reprova salvo decisão registrada do usuário, e Minor vira pendência aceita.
- A Parte I diz qual é o problema e como se convive com ele hoje. Revisada na seção 6 (necessidade × jornada de hoje). É o portão mais barato de todos, e o único que pergunta contra que problema isto está sendo construído.
- A Parte II diz como se usa o que vai existir. Revisada na seção 8 (jornada × viewports).
- A Parte III diz como se constrói. Revisada na seção 16 (segurança × clareza técnica) — autorização, isolamento, e se sobrou decisão para quem vai codar.
- A implementação é medida contra as três, na seção 17 (aderência × qualidade × testes) — uma revisão por fase, e uma da branch inteira antes de fechar.
Cada parte só se escreve depois que a anterior passou. Descrever tela sobre problema mal enquadrado, ou detalhar endpoint sobre experiência vaga, é o jeito mais caro de descobrir que era outra coisa.
Parte I — O problema¶
Aqui não entra solução: nem tela, nem endpoint, nem botão. Só o que dói, para quem, e o que se faz hoje. Se você já sabe qual é a tela, guarde para a Parte II.
1. Dor¶
O dossiê da garantia continua aberto depois que ela passa a valer. Registrado o contrato assinado, a garantia vira o compromisso da garantidora com o inquilino — e os arquivos que a sustentam seguem editáveis: dá para anexar outro, trocar o conteúdo de um já anexado e apagar qualquer um, sem que nada no estado da garantia mude.
O que isso custa aparece no dia em que alguém precisa da garantia:
- No sinistro, o que se lê pode não ser o que foi aceito. A imobiliária comunica a inadimplência e a garantidora vai ao dossiê conferir o que assinou. Se o arquivo pôde ser trocado depois, o PDF que está lá não prova o que estava lá quando a garantia passou a valer — e a garantidora paga (ou recusa) sobre um documento que ela não consegue datar.
- A troca não deixa traço nenhum na tela. O cartão do documento mostra nome, tamanho e a data de criação do registro, que a substituição não altera; a data de troca do conteúdo existe no banco e não é exibida em lugar nenhum do app. A linha de histórico "Documento substituído" só é emitida para documento de análise de crédito — em documento de garantia não há linha nenhuma. Sobra o log de auditoria, e
audit:listé só doADMIN_TENANT: oOPERATORe oANALYST, que abrem a garantia no dia a dia, não o veem. - Apagar pede confirmação, e nada mais. O diálogo pergunta "deseja excluir?" e avisa que não dá para desfazer — mas não há guarda de estado nenhuma: confirmado, o anexo sai da garantia vigente. Só o documento apontado como o contrato assinado é protegido — e apenas ele, e apenas enquanto a garantia está vigente ou vencida: na encerrada, apagá-lo é aceito de propósito (RN13c da spec 0038).
É a mesma família de defeito que a spec 0038 tratou no estado: antes dela, a garantia nascia vigente e a cobrança abria antes de existir contrato. O estado foi fechado; o dossiê não.
2. Quem sente¶
Quem paga a conta é a garantidora, e sempre no pior momento — no sinistro, que é quando o documento é cobrado.
Exposição e consumo são coisas diferentes, e as duas importam. A exposição é obrigatória e vale para todas: toda garantia que passa a valer fica com o dossiê aberto. O consumo é exceção — só a garantia que chega a sinistro tem os arquivos conferidos —, e é justamente por ser raro que o defeito sobrevive: ninguém olha o dossiê até o dia em que ele é a única prova.
E a conta não para só na garantidora: quem recebe a indenização é a imobiliária, que repassa ao locador. Uma recusa apoiada em documento que não se consegue datar cai sobre ela primeiro.
Quem pode mexer, hoje, na garantia vigente:
| Papel | Anexar | Trocar o arquivo | Excluir |
|---|---|---|---|
ADMIN_TENANT (gestor da garantidora) | sim | sim | sim |
OPERATOR (cadastros e criação de análises) | sim | sim | sim |
AGENCY_USER (imobiliária) | não — fechado pelo fix 0011 | não | não |
ANALYST, VIEWER | não (só leem) | não | não |
ADMIN_GLOBAL (plataforma) | sim | sim | sim — e sem filtro de tenant |
A imobiliária foi fechada ontem, e por outro motivo: ela não escreve em garantia, em estado nenhum. A porta que sobra é a de dentro da garantidora — e é a que ninguém está olhando, porque quem tem essas permissões é justamente quem se confia.
Não há operação para medir. O produto está no ar desde a spec 0028 sem nenhum cliente, e sinistro nenhum aconteceu; o alcance aqui é de desenho, não de volume: toda garantia que passar a valer fica exposta, porque a porta é a mesma para todas.
3. A jornada de hoje¶
Como o dossiê fecha hoje — e ele não fecha¶
- A garantia é emitida na análise aprovada e nasce aguardando assinatura.
- Alguém da garantidora anexa o contrato assinado no painel de documentos da garantia.
- Registra a assinatura apontando aquele arquivo. A garantia passa a vigente, e a cobrança é liberada.
- Daqui em diante nada trava. O painel de documentos continua com "Adicionar", e cada cartão continua com substituir e excluir. Medido no dev-remote, numa garantia levada a vigente para o teste, com o token do gestor: anexar outro documento devolve
201, trocar o conteúdo dele devolve200e excluir devolve204. - A única exceção é o próprio contrato apontado: trocá-lo ou apagá-lo devolve
422 SIGNED_DOCUMENT_IN_USE— medido. Todos os outros anexos seguem livres, e o caminho para mexer no contrato existe: aponte outro documento pela correção do registro, e o antigo deixa de estar em uso. Na garantia encerrada nem essa proteção vale: ali a exclusão do contrato é liberada de propósito.
O que se faz fora do sistema¶
Na montagem do dossiê, nada — e é esse o ponto. Não há conferência combinada, planilha paralela nem cópia guardada noutro lugar: quem abre a garantia acredita no que está no painel, porque não existe outro lugar para olhar. O sistema é a única fonte, e ela é editável.
O sinistro, que é onde a dor aparece, acontece hoje inteiramente fora do produto — não há módulo de sinistro, só os parâmetros de carência e prazo na garantidora. É limite declarado, não lacuna desta spec: quando o sinistro entrar, ele vai perguntar ao dossiê, e a resposta precisa já ser confiável.
O que já se tentou¶
- Fechar o estado, na spec 0038. A garantia deixou de nascer vigente, ganhou o registro de assinatura como único ato que a ativa, e o documento apontado passou a ser protegido de troca e exclusão (
SIGNED_DOCUMENT_IN_USE). Fechou o contrato — não o dossiê. - Fechar a porta da imobiliária, no fix 0011. Ela não escreve mais em documento de garantia, em estado nenhum. Fechou um papel — não o estado.
As duas tentativas mostram o padrão: o dossiê foi tratado peça por peça, e nunca pelo estado da garantia, que é a pergunta que o sinistro faz.
4. Resultado esperado¶
Depois que a garantia passa a valer, alterar o dossiê deixa de ser rotina e vira exceção — e exceção que aparece. O caminho não é proibir para sempre: é fechar por padrão, abrir por permissão nomeada, e registrar cada alteração onde quem confere consegue ver.
Três coisas mudam (decisões do usuário, 2026-08-19):
- Fecha por padrão. Numa garantia vigente, vencida ou encerrada, ninguém anexa, troca nem exclui arquivo pelo caminho comum de documento — o
documents:create/update/deleteque serve ao resto do sistema deixa de bastar aqui. - Duas permissões abrem, e são só duas. Uma para acrescentar arquivo ao dossiê fechado; outra para trocar ou excluir. São permissões próprias, separadas de propósito: acrescentar faz o dossiê crescer, trocar e excluir apagam o que sustentava a garantia, e quem administra a garantidora pode querer conceder uma e não a outra. Gestor e operador nascem com as duas; os demais papéis, com nenhuma.
- Toda alteração fica visível na garantia. Quem, quando e o quê, na tela da garantia, para quem lê a garantia — e não só no log de auditoria, que hoje é o único registro e que
OPERATOReANALYSTnão enxergam. É a metade da dor que a trava sozinha não resolveria: sem o histórico, um dossiê alterado por quem tem permissão volta a ser indistinguível de um intocado.
Como se sabe que resolveu, sem métrica instrumentada:
- Quem não tem as permissões novas não consegue mexer no dossiê de uma garantia que já passou a valer — nem tentando por fora da tela.
- Quem tem consegue, e a alteração aparece na garantia com autor e data, sem precisar de
audit:list. - Quem abre a garantia no sinistro sabe dizer se o dossiê mudou depois da ativação — hoje não sabe, porque a substituição não deixa traço visível nenhum.
- Antes de assinar continua livre: aguardando assinatura é quando o dossiê se monta, e travar ali impediria anexar o próprio contrato que a assinatura precisa.
Duas perguntas que a Parte III precisa responder, e que não são desta seção: se a permissão nova atravessa a proteção do contrato apontado (SIGNED_DOCUMENT_IN_USE) ou empilha com ela; e o que vale para o ADMIN_GLOBAL, que recebe toda permissão nova por construção do seed e que a fronteira plataforma × cliente manda deixar de fora.
O caminho anterior era congelar, e mudou. Até 2026-08-19 esta spec dizia "imutável, com o gestor podendo apenas acrescentar". Caiu por um efeito que a revisão expôs: congelar deixava sem conserto o contrato apontado por engano — e "encerrar e emitir de novo" não existe, porque há uma garantia por análise de crédito, com restrição no banco. Rastrear em vez de proibir resolve a mesma dor sem criar um beco.
5. Fora de escopo¶
- Corrigir o registro do contrato assinado. Continua como a spec 0038 desenhou (RN10), sem o limite de "só documento anexado antes da ativação" que uma versão anterior desta spec propôs: com permissão nomeada e histórico visível, a troca do ponteiro é rastreável, que é o que aquele limite tentava garantir travando.
- As notas da garantia. A observação continua editável na vigente — o pedido é sobre arquivo, e a nota já trava sozinha quando a garantia vence ou é encerrada.
- Encerrar a garantia. Continua valendo em qualquer estado que não seja encerrada: é registro de decisão, não alteração de dossiê.
- Cobranças. Nascem justamente com a garantia vigente; travá-las apagaria o faturamento.
- A exclusão do contrato apontado na garantia encerrada é fechada junto, e isso é reversão consciente. Hoje ela é liberada de propósito (RN13c da spec 0038), sob o argumento de que garantia encerrada não precisa mais do papel. Esta spec fecha os três estados pela mesma régua — a pergunta passa a ser do estado do dossiê, não do papel de cada documento — e quem tiver a permissão de trocar/excluir segue conseguindo, com o histórico registrando.
- Versionar documento, ou guardar cópia congelada. O histórico diz que mudou, quem mudou e quando — não guarda a via anterior. Recuperar o arquivo substituído é outro problema.
- Backfill das garantias vigentes sem contrato apontado. São 128 no dev-remote e nenhuma na produção — dado de teste anterior à spec 0038. Decisão do usuário: não tratar. Com as permissões novas elas seguem consertáveis por quem as tem.
- A mesma pergunta na análise de crédito. O dossiê da análise tem a janela da imobiliária (spec 0036 e fix 0011) e não é o que este pedido trata.
- Assinatura digital, hash ou carimbo de tempo do arquivo. Provar que o PDF é o original é outro problema, e maior.
- Alertar alguém quando o dossiê muda. O histórico é consultável; notificação ativa é escopo de outra spec.
6. Revisão do problema¶
Preenchido pelo laço do /spec new, rubrica .claude/rubrics/problema.md. Registrar todas as rodadas, inclusive as reprovadas.
| Rodada | Critical | Important | Minor | Veredicto |
|---|---|---|---|---|
| 1 | 1 | 1 | 3 | reprovada — laço interrompido por [PERGUNTAR AO USUÁRIO] |
| 2 | 0 | 3 | 4 | reprovada — laço interrompido por [PERGUNTAR AO USUÁRIO] |
| 3 | 0 | 1 | 5 | reprovada — a trava criava um beco sem saída; o usuário mudou o caminho |
| 4 | 0 | 0 | 3 | aprovada |
Resultado: aprovada na rodada 4 — três reprovações antes, duas delas interrompidas para decisão do usuário e a terceira porque o caminho mudou.
Pendências aceitas: os três Minor da rodada 4 apontam para conteúdo que a Parte III escreve de qualquer jeito. Dois deles já estão registrados no fim da seção 4 (a colisão com SIGNED_DOCUMENT_IN_USE e o ADMIN_GLOBAL que recebe toda permissão nova); o terceiro era o slug, e foi resolvido renomeando a spec e a branch.
Parte II — A experiência¶
Não preencher antes da revisão da seção 6 estar aprovada.
7. Jornada pretendida¶
Ponto de entrada: a tela da garantia, no painel de documentos — o mesmo lugar de hoje. Ninguém navega até aqui para "alterar um dossiê fechado": a pessoa abre a garantia por outro motivo (conferir, cobrar, atender a imobiliária) e se depara com o painel. É por isso que a tela precisa dizer o estado do dossiê antes de ela tentar.
Jornada, caso feliz — e são duas, porque as pessoas são duas:
Quem não tem as permissões novas (a maioria):
- Abre a garantia e desce até Documentos.
- Lê, no lugar onde havia o botão "Adicionar", por que o dossiê está fechado.
- Consulta e baixa o que precisa. Cada documento continua abrindo no visualizador.
- Se o dossiê foi alterado depois que a garantia passou a valer, ela vê isso ali mesmo — sem precisar de permissão de auditoria, e sem precisar perguntar a ninguém.
Quem tem (gestor e operador):
- Abre a garantia e desce até Documentos. O painel também diz que o dossiê está fechado — a diferença é que oferece a ação, não a esconde.
- Anexa o documento novo pelo mesmo formulário de sempre, ou substitui/exclui pelo ícone do cartão. O aviso de que aquilo fica registrado é a própria linha do cabeçalho — ela está à vista o tempo todo. Não se cria diálogo novo para anexar nem para substituir: o primeiro não tem confirmação hoje e o segundo dispara o seletor de arquivo direto do ícone, e inventar um passo cobraria um clique a mais de quem já sabe o que está fazendo. A exceção é excluir, que já tem confirmação e ganha ali a menção ao histórico — é a única ação destrutiva.
- Confirma. Um aviso curto confirma a gravação, e a alteração entra no histórico, no topo, com autor e data.
Custo de uso: nenhum campo novo a preencher. O sistema já sabe quem está alterando, quando, qual documento e qual era o estado da garantia — nada disso é perguntado. Não se pede justificativa: exigir um texto por anexo transformaria uma exceção rara em formulário, e a spec 0038 já mostrou que motivo obrigatório se paga onde a ação é destrutiva e definitiva (encerrar), não onde ela é rastreada.
7.1 Desktop¶
O painel de documentos fica onde está, abaixo de Cobranças, e ganha duas mudanças visíveis.
No cabeçalho do painel, uma linha com o estado do dossiê — texto secundário com ícone de cadeado à esquerda, no peso do blockedReason de hoje e não em tom de alerta: é condição permanente da garantia, não erro, e alerta permanente vira ruído que ninguém lê. Ocupa a largura logo abaixo da contagem (1/20, que não aparece quando o dossiê está vazio — a linha aparece do mesmo jeito) — e não no slot da direita, que hoje é ou o botão "Adicionar" ou a mensagem de bloqueio, um ou outro. Quem tem permissão precisa dos dois ao mesmo tempo: a frase (que pode chegar a ~90 caracteres) embaixo, o botão à direita, como está:
- "Dossiê fechado — a garantia está vigente. Alterações ficam registradas no histórico." para quem tem as permissões;
- "Dossiê fechado — a garantia está vigente e os arquivos não podem mais ser alterados." para quem não tem. A frase fica no mesmo lugar nos dois casos — a linha de largura cheia —; o que muda é o slot da direita, onde o botão "Adicionar" aparece para quem pode e some para quem não pode.
O texto nomeia o estado real (vigente, vencida, encerrada), porque "fechado" sozinho não diz desde quando nem por quê.
As permissões são duas e podem ser concedidas separadas, então a faixa e os controles têm quatro combinações — mais a da imobiliária, que não é combinação e sim outra regra:
| Quem abre | Faixa | Oferece |
|---|---|---|
| tem as duas | "Dossiê fechado — a garantia está vigente. Alterações ficam registradas no histórico." | anexar · substituir · excluir |
| só pode acrescentar | "Dossiê fechado — a garantia está vigente. Você pode anexar; alterações ficam registradas." | anexar |
| só pode trocar/excluir | "Dossiê fechado — a garantia está vigente. Você pode corrigir os anexos; alterações ficam registradas." | substituir · excluir |
| nenhuma das duas | "Dossiê fechado — a garantia está vigente e os arquivos não podem mais ser alterados." | nada |
| imobiliária | continua no slot da direita, como hoje: "A garantia é da garantidora". Não ganha a linha nova — para ela nada mudou, e a frase dela é mais restritiva (não escreve em garantia nenhuma, em estado nenhum) | nada |
Uma seção nova, "Histórico do dossiê", logo abaixo do painel de documentos — não dentro dele: o painel é o que existe agora, o histórico é o que aconteceu. Cada linha traz o que mudou, o arquivo, quem e quando, em ordem decrescente:
Documento anexado · contrato-aditivo.pdf · Marcos (gestor) · 19/08/2026 14:32
Arquivo renomeado · comprovante.pdf → comprovante-2024.pdf · Ana (operadora) · 18/08/2026 09:13
Observação editada · comprovante.pdf · Ana (operadora) · 18/08/2026 09:12
Documento substituído · comprovante.pdf · Ana (operadora) · 18/08/2026 09:10
Contrato assinado corrigido · agora aponta contrato-final.pdf · Marcos (gestor) · 17/08/2026 11:02
Documento excluído · rascunho.pdf · Ana (operadora) · 17/08/2026 16:44
"Arquivo renomeado" não tem botão na tela. O
PATCHda API aceita trocar o nome, e nenhum controle do painel o faz hoje — a linha existe porque o evento é possível pela API, e um histórico que só conhece o que a tela oferece deixa de fora exatamente quem entrou por fora.
Renomear e editar observação mostram o par de → para, como o histórico da análise já faz: nesses dois o dado que identifica a mudança é justamente o texto, e só o valor novo não deixa ninguém comparar. Nos outros quatro o nome do arquivo basta.
Os seis tipos de evento são os seis caminhos de escrita, e não é coincidência: o que exige permissão gera linha. Editar observação e renomear entram porque mudam o que o dossiê diz sobre um documento; a correção do contrato apontado entra porque é ela que decide qual arquivo o sinistro procura primeiro — e a seção 5 só a mantém liberada porque o histórico a torna rastreável.
A seção só aparece quando há alteração registrada — garantia cujo dossiê não mudou depois de passar a valer não mostra seção vazia, e essa ausência é a informação: nada mudou.
A imobiliária vê a seção, sem os nomes (decisão do usuário, 2026-08-19): para ela cada linha é "Documento substituído · comprovante.pdf · 18/08/2026", sem autor. Ela precisa saber que o arquivo que baixou pode estar desatualizado; quem mexeu é operação interna da garantidora, e a spec 0038 já escondeu dela a nota interna e o motivo do encerramento pelo mesmo princípio.
O padrão visual é o Historico do design system, o mesmo da análise de crédito (timeline vertical com ícone por tipo de evento). Não se inventa padrão — mas vale saber que o componente de hoje vive dentro da análise e é tipado aos eventos dela: reusar aqui é extrair, não importar.
Sem teto e sem "ver mais": o histórico da análise precisou de agrupamento e limite porque uma devolução gera dez linhas de uma vez; aqui cada linha é uma exceção que alguém decidiu cometer, e uma garantia com trinta linhas é informação, não ruído — a lista longa é o alerta.
Os controles que o painel tem, e de que lado cada um cai:
| Controle | Com o dossiê fechado |
|---|---|
| visualizar, baixar | sempre — leitura não é alteração |
| "Adicionar" (formulário de anexo) | exige a permissão de acrescentar |
| substituir (ícone do cartão) | exige a de trocar/excluir |
| excluir (ícone do cartão) | exige a de trocar/excluir |
| "Anexar outra via", no visualizador de arquivo que não abre | é um substituir com outro nome — mesma permissão |
| lápis "Editar observação" e "+ observação" no cartão | exige a de trocar/excluir: mudam o que o dossiê diz sobre um documento que já está lá, e o mesmo PATCH renomeia o arquivo |
A observação do documento não se confunde com as notas da garantia, que a Parte I deixou fora de escopo: aquelas continuam editáveis na vigente, por outro caminho e outra permissão.
O caminho "enviar arquivo" do diálogo de correção gera duas linhas — "Documento anexado" e "Contrato assinado corrigido" —, porque são dois fatos: entrou arquivo novo no dossiê, e o ponteiro do contrato mudou. Juntar num evento só esconderia metade de quem lê procurando o que entrou.
A segunda porta de escrita fica no diálogo de corrigir o contrato assinado, e ela é fácil de esquecer: além de "escolher um já anexado", ele tem um caminho "enviar arquivo", que anexa um documento novo na garantia. Com o dossiê fechado, esse caminho exige a permissão de acrescentar (decisão do usuário, 2026-08-19) — anexo é anexo, venha por onde vier. Quem tem guarantees:sign e não tem a de acrescentar continua corrigindo, apontando um documento que já está no dossiê.
O caminho "enviar arquivo" fica desabilitado — nos dois viewports —, e não sumido. É a exceção à regra de "não oferecer o que vai ser negado", e ela tem motivo: aqui a opção desabilitada é a explicação. Sumindo, quem não encontra o arquivo certo na lista fica sem entender por que só existe meio diálogo; desabilitada com a frase ao lado, ela diz o que falta e a quem pedir: "Você não tem permissão para anexar arquivo numa garantia que já passou a valer. Escolha um documento já anexado, ou peça a um gestor da garantidora."
E há um beco que precisa de texto próprio: dossiê fechado, sem nenhum anexo, e alguém que pode assinar mas não pode acrescentar. Os dois caminhos ficam indisponíveis — "escolher um já anexado" já nasce desabilitado quando não há anexo. O diálogo então não oferece nada, e diz o porquê: "Esta garantia não tem documento anexado para apontar, e o dossiê está fechado. Peça a um gestor da garantidora para anexar o contrato." É o retrato exato das garantias antigas sem contrato apontado, e é onde a pessoa desiste se a tela não falar.
A confirmação de exclusão continua como está — ela já existe e já avisa que não dá para desfazer. Ganha uma linha a mais: "Esta exclusão fica registrada no histórico da garantia." É a única ação destrutiva do conjunto, e é onde o aviso se paga.
Dossiê fechado e cheio (20/20): as duas coisas convivem, porque ocupam lugares diferentes — a linha do dossiê fechado embaixo, o "Limite atingido" no slot da direita, onde estaria o "Adicionar". Some a faixa seria tirar de quem tem trocar/excluir o aviso de que a alteração fica registrada, justamente quando a saída oferecida é excluir alguma coisa.
Nada desta feature vaza para as outras telas que usam o mesmo painel — inquilino, imóvel e análise de crédito. Vale para os três acréscimos: a linha do dossiê fechado, o aviso de sucesso e a frase "esta exclusão fica registrada no histórico da garantia" no diálogo de confirmação. Todos são condicionados ao painel da garantia; o DocumentsPanel é compartilhado e hoje não emite aviso de sucesso em lugar nenhum.
O que continua igual: o selo de contrato assinado, a ordem que põe o contrato no topo, e o limite de 20 documentos.
Quem concede as duas permissões lê isto, na tela de Perfis de Acesso, onde cada permissão é uma caixa com rótulo e descrição:
| Rótulo | Descrição |
|---|---|
| Anexar em garantia fechada | Permite acrescentar documentos a uma garantia que já passou a valer. O que já está no dossiê não é afetado, e cada anexo fica registrado no histórico da garantia. |
| Alterar ou remover documento de garantia fechada | Permite substituir o arquivo, editar a observação e excluir documentos de uma garantia que já passou a valer. Apaga ou reescreve o que sustentava a garantia — cada alteração fica registrada no histórico. |
Os dois rótulos são lidos lado a lado, numa lista de caixas, e é a única coisa que sustenta a decisão de conceder uma e não a outra: por isso o segundo diz o que ele destrói, e não só o que ele permite.
7.2 Mobile (375px)¶
A tela da garantia em 375px é uma pilha de seções colapsáveis (MobileDetailSection), e as duas mudanças entram nela sem inventar padrão:
- A faixa de estado do dossiê aparece dentro da seção "Documentos", acima do primeiro cartão, ocupando a largura. É texto de duas linhas no máximo — em 375px a frase completa ("Dossiê fechado — a garantia está vigente e os arquivos não podem mais ser alterados") quebra em duas e não trunca: truncar o motivo é o defeito que o fix 0010 registrou.
- "Adicionar" some para quem não tem a permissão, como já acontece hoje para a imobiliária — é o mesmo mecanismo, com outra condição.
- Nos cartões, os ícones de substituir e excluir somem para quem não tem a segunda permissão. Sobram visualizar e baixar, e os dois alvos de 44px passam a dividir a faixa de ações em duas metades, sem buraco.
- O histórico do dossiê vira uma seção colapsável própria, "Histórico do dossiê", logo depois de "Documentos" na pilha (Cobertura · Partes · Vigência · Sinistro · Notas · Cobranças · Documentos · Histórico do dossiê), fechada por padrão e com a contagem no título (
Histórico do dossiê (3)). Fechada porque quem abre a garantia no celular está atrás do número da cobrança ou do contato, não do histórico; e a contagem no título é o que diz, sem abrir, que houve alteração. -
Cada linha do histórico quebra em duas no celular: a ação e o arquivo em cima, autor e data embaixo em corpo menor. Uma linha só forçaria truncar o nome do arquivo, que é o dado que identifica o que mudou.
-
A confirmação de que deu certo é o aviso curto no rodapé da tela (o
toastque a garantia já usa ao registrar assinatura), e não a linha nova no histórico: em 375px ela cai dentro de uma seção fechada, abaixo de Documentos, e ninguém a veria.
Nada desta feature deixa de aparecer no celular. A garantia é consultada em visita e em atendimento — é onde a pergunta "esse dossiê mudou?" mais aparece.
7.3 Estados¶
Sem permissão (o caso mais comum). O painel não oferece o que vai ser negado: sem "Adicionar", sem substituir, sem excluir. No lugar, a frase do estado do dossiê. Quem insistir por fora da tela recebe a recusa da API com o mesmo texto — a tela é conveniência, não a trava.
Dossiê aberto (aguardando assinatura). Nada muda em relação a hoje: sem faixa, sem histórico, com todos os botões. É a metade que impede a feature de virar "bloqueia sempre".
Sem alteração nenhuma. A seção de histórico não aparece — no desktop e no mobile. É o estado da esmagadora maioria das garantias, e mostrar "nenhuma alteração" em toda garantia seria ruído permanente para informar exceção.
Carregando. O histórico é uma chamada própria, e o painel de documentos não espera por ela — quem abriu a garantia para baixar um arquivo não fica parado por causa de uma lista de eventos. Enquanto a resposta não chega, a seção não existe na tela: nada de esqueleto. É a escolha certa aqui porque a esmagadora maioria das garantias não tem alteração nenhuma, e um esqueleto que aparece e some em todas elas seria uma piscada permanente para anunciar ausência.
Erro ao alterar. A recusa vai para o alerta que o painel já tem no topo do corpo — não se cria erro por cartão —, com o motivo em português e a saída: "Você não tem permissão para alterar o dossiê de uma garantia vigente. Peça a um gestor da garantidora." Nunca um número de status: é a lição do fix 0010, onde um 413 chegava à tela como "Erro 413".
Erro ao carregar o histórico. A seção aparece — é a exceção à regra de só existir quando há alteração, porque nesse momento ninguém sabe se há — com o título sem contagem ("Histórico do dossiê"), aberta no celular em vez de colapsada, dizendo que não conseguiu carregar e oferecendo tentar de novo. E o painel de documentos continua funcionando. Um histórico que não carrega não pode derrubar a consulta ao dossiê, que é o motivo pelo qual a pessoa abriu a tela.
Tem a permissão e ainda assim é recusado. É o caso do documento apontado como contrato assinado, que a spec 0038 protege por si (SIGNED_DOCUMENT_IN_USE). Os dois desfechos possíveis já têm tela, e a Parte III só escolhe qual:
- Se a permissão nova não atravessa a proteção (o desfecho provável), a recusa aparece com o motivo dela — que é outro — e o caminho: corrigir o registro da assinatura, não apagar o arquivo.
- Se atravessa, a garantia pode ficar vigente com o contrato apontado apagado. Aí o lugar do selo "Contrato assinado" passa a mostrar "O contrato assinado foi removido do dossiê", em tom de alerta, e o diálogo de correção abre com o caminho de apontar outro documento — porque o estado é recuperável e a tela não pode deixá-lo mudo.
A garantia foi encerrada enquanto a tela estava aberta. A alteração é recusada pela API com o estado real, e a tela não finge que deu certo: mostra a recusa e recarrega o painel.
8. Revisão da experiência¶
Preenchido pelo laço do /spec new, rubrica .claude/rubrics/experiencia.md. Registrar todas as rodadas, inclusive as reprovadas.
| Rodada | Critical | Important | Minor | Veredicto |
|---|---|---|---|---|
| 1 | 0 | 5 | 4 | reprovada — laço interrompido por [PERGUNTAR AO USUÁRIO] |
| 2 | 0 | 3 | 4 | reprovada — laço interrompido por [PERGUNTAR AO USUÁRIO] |
| 3 | 0 | 2 | 5 | reprovada |
| 4 | 0 | 0 | 8 | aprovada |
Resultado: aprovada na rodada 4.
Pendências aceitas: dos oito Minor da rodada 4, sete foram fechados no próprio texto (a frase da imobiliária, o par de → para, o texto literal da recusa no diálogo, as duas linhas do "enviar arquivo", a ressalva do diálogo de exclusão na jornada, o título da seção em erro e o peso visual da linha de estado). Fica aceito o oitavo: a Parte II cita guarantees:sign, SIGNED_DOCUMENT_IN_USE e o PATCH da API — resíduo técnico numa parte que deveria falar só de experiência, e que some quando a Parte III assumir esses nomes.
Parte III — Detalhamento¶
Não preencher antes das revisões das seções 6 e 8 estarem aprovadas.
9. Regras de negócio¶
RN1 — O dossiê fecha quando a garantia passa a valer. Toda escrita sobre documento cujo dono é uma garantia em ACTIVE, EXPIRED (derivado) ou TERMINATED é recusada, salvo pelas permissões da RN3. Em PENDING_SIGNATURE nada muda: é quando o dossiê se monta.
RN2 — A regra é do estado da garantia, não do papel do documento. Vale para qualquer documento cujo guarantee_id aponte para a garantia fechada — inclusive os que hoje não têm proteção nenhuma, e inclusive o contrato apontado, que já tinha a sua (RN13c da 0038).
RN3 — Duas permissões abrem, e só elas.
| Permissão | Abre |
|---|---|
guarantees:dossier_attach | POST /documents com entity_type=guarantee numa garantia fechada — inclusive quando quem chama é o diálogo de assinatura pelo caminho "enviar arquivo", que é o mesmo endpoint |
guarantees:dossier_amend | PUT /documents/{id}/content, PATCH /documents/{id} e DELETE /documents/{id} numa garantia fechada |
Ter a permissão nova não dispensa a de base: quem não tem documents:create continua sem anexar em lugar nenhum. As novas são um segundo portão, não um atalho.
RN4 — As permissões da RN3 não atravessam a proteção do contrato apontado. O documento marcado como contrato assinado continua recusando troca e exclusão com SIGNED_DOCUMENT_IN_USE enquanto a garantia estiver vigente ou vencida — o caminho para trocá-lo é corrigir o registro da assinatura, e essa ordem é o que impede a prova principal de sumir por um clique. Muda uma coisa: na garantia encerrada, onde hoje a exclusão dele é liberada, ela passa a exigir guarantees:dossier_amend como qualquer outro documento.
RN4a — O SaaS admin é barrado por guarda de código, e só onde ele hoje alcança. As rotas em que ele consegue escrever no dossiê passam a recusá-lo com 403 PLATFORM_ADMIN_CANNOT_AMEND — em qualquer estado, inclusive PENDING_SIGNATURE, e não só no dossiê fechado: a fronteira é sobre de quem é o dado, não sobre quando. E o mesmo vale para as duas rotas de assinatura — POST /guarantees/{id}/signature, que aponta o contrato e torna a garantia vigente, e PUT /guarantees/{id}/signature, que o reaponta. Fechar só a correção deixaria a porta principal destrancada com as janelas fechadas. Isso estreita um risco aceito da spec 0039, que registrou "registrar assinatura, encerrar e anotar continuam alcançáveis pelo ADMIN_GLOBAL" — decisão do usuário, 2026-08-19; encerrar e anotar seguem como estão, e são assunto de outra spec.
Onde a guarda mora, e por que não é "antes de tudo" nas três rotas por id. As rotas PUT /content, PATCH e DELETE recebem só o doc_id: saber que aquele documento é de uma garantia exige carregá-lo — e carregar filtra por tenant, que para o ADMIN_GLOBAL é None sobre coluna NOT NULL. Medido: hoje ele já toma 404 ali, e no POST /documents toma 500 (IntegrityError ao gravar tenant_id nulo). Ou seja, nas quatro rotas de documento ele já não escreve — mal, mas não escreve.
Então a RN4a é cirúrgica, e não uma guarda global:
| Rota | Hoje | Depois |
|---|---|---|
POST /documents com entity_type=guarantee | 500 (IntegrityError) | 403 PLATFORM_ADMIN_CANNOT_AMEND, logo depois de resolver a entidade no escopo |
PUT /content, PATCH, DELETE | 404 (o documento não é alcançável) | não muda — 404 continua sendo resposta correta, e forçar 403 exigiria dar a ele leitura que ele não tem |
POST e PUT /guarantees/{id}/signature | funciona — _get_authorized pula o filtro quando o tenant é nulo | 403 PLATFORM_ADMIN_CANNOT_AMEND, antes de resolver o documento |
É nas duas rotas de assinatura que está o buraco de verdade, e é lá que a guarda nomeada muda comportamento. Nas de documento ela troca um erro feio por um erro correto no POST, e deixa o 404 das outras três como está.
A recusa vale independentemente das permissões que ele tenha — é a mesma forma da RN13 da spec 0039 (PLATFORM_ADMIN_CANNOT_EMIT), e pelo mesmo motivo: o conjunto dele é montado como todas as permissões declaradas, e a fixture de teste concede todas sem consultar o seed. Trancar por subtração de permissão seria uma trava que passa quando alguém concede a permissão de volta. A subtração no seed continua, como reforço e como sinal na tela de perfis — mas quem recusa é o código.
RN5 — A ordem das guardas é: quem escreve, onde escreve, o que escreve. Primeiro o SaaS admin (RN4a), depois a janela da imobiliária (fix 0011), depois o dossiê fechado (RN1), depois o contrato apontado (RN4). A imobiliária nunca chega a ouvir falar em dossiê fechado — ela não escreve em garantia, ponto —, e quem não tem a permissão nova ouve isso antes de ouvir sobre o contrato.
Nas duas rotas de assinatura a guarda da RN4a é a primeira de todas — como na spec 0039, que a pôs antes de resolver a FK e mediu isso com id inexistente; ali o ADMIN_GLOBAL alcança a garantia de qualquer cliente, então resolver primeiro só trocaria o 403 por um 200.
No POST /documents ela vem logo depois da resolução no escopo, e nas outras três rotas de documento não existe — a RN4a explica por quê.
Para todo o resto, antes das guardas vem o escopo. No POST /documents, entity_type e entity_id chegam no corpo: a guarda do dossiê só roda depois de _validate_entity/resolve_in_scope resolver a garantia no escopo de quem chama. Nas outras três, depois do get_with_content / get_metadata escopado. Invertida a ordem, o 403 novo passaria a distinguir "existe e está fechada" de "não existe", e o entity_id de outra garantidora viraria oráculo de existência — a enumeração que a RN4 da spec 0034 fechou. Fora do escopo continua sendo 404.
RN6 — Toda alteração no dossiê fechado gera registro consultável na garantia. Seis eventos: documento anexado, substituído, excluído, observação editada, arquivo renomeado e contrato assinado corrigido. Os dois de texto trazem o par de → para.
RN6a — Os seis eventos de tela saem de cinco ações de log, e o mapa é este:
Ação em audit_logs | Evento na tela |
|---|---|
DOCUMENT_UPLOADED | Documento anexado |
DOCUMENT_REPLACED | Documento substituído |
DOCUMENT_DELETED | Documento excluído |
SIGNATURE_CORRECTED | Contrato assinado corrigido |
DOCUMENT_UPDATED, com notes alterada | Observação editada |
DOCUMENT_UPDATED, com original_filename alterado | Arquivo renomeado |
Um PATCH que muda os dois campos gera as duas linhas — são dois fatos, e escondê-los num evento só obrigaria a inventar um rótulo que não descreve nenhum. Um PATCH que não muda valor nenhum não gera linha: hoje o log é gravado mesmo assim, e a leitura do histórico descarta eventos cujo de e depois são iguais. Os eventos anteriores a esta spec não têm antes/depois no payload e aparecem como "Documento alterado", sem o par — é o que existe, e inventar valor para preencher a coluna seria mentir sobre o passado.
RN7 — O histórico sai do log de auditoria que já existe. Não há tabela nova: as cinco ações já são gravadas em audit_logs com entity_type="guarantee". O que muda é uma leitura própria, filtrada por garantia e por essas ações, exposta a quem lê a garantia — sem exigir audit:list, que é do administrador e é justamente o que hoje torna o registro invisível.
RN8 — O corte é o registro da assinatura (inclusive ele), e quando não existe a lista é inteira. A comparação é created_at >= a do SIGNATURE_REGISTERED, e o desempate é o próprio evento: o que empatar no mesmo segundo entra. Não é detalhe — o created_at de audit_logs tem resolução de segundo no SQLite da suíte, e um teste que anexa, registra a assinatura e anexa de novo empata os três; com > o evento que a regra quer mostrar sumiria, e a instabilidade seria a mesma dos dois testes intermitentes que o CLAUDE.md registra. Mostrar a mais é o erro seguro: o histórico existe para não esconder alteração. O histórico mostra o que aconteceu depois do SIGNATURE_REGISTERED daquela garantia — mexer durante a montagem do dossiê era o esperado, e listar isso afogaria a exceção no ruído.
Em PENDING_SIGNATURE o endpoint devolve lista vazia e truncated_at_activation: true — o corte existe, é o próprio fato de a garantia não ter passado a valer —, e a seção não aparece: o dossiê está aberto, mexer nele é o esperado, e não há nada de excepcional a mostrar. É o que a Parte II descreve como "dossiê aberto — nada muda".
Fechado, e sem registro de ativação, não há corte: a lista sai completa, com uma linha dizendo "esta garantia não tem registro de quando passou a valer; a lista mostra todas as alterações". São dois casos reais, e nenhum deles é hipotético: as garantias anteriores à spec 0038, que nasceram vigentes sem passar pelo registro (128 no dev-remote), e a garantia encerrada direto de PENDING_SIGNATURE, que a RN15 da 0038 permite. Nos dois o dossiê está fechado pela RN1 — alterar exige permissão — e seria absurdo exigir permissão para uma alteração que o histórico não mostraria.
RN9 — A imobiliária lê o histórico sem os nomes e sem o texto anterior. (O critério aqui é partner_agency_id; no histórico da análise, que é de onde o componente será extraído, o corte de nomes é decisions:read — então o OPERATOR, que não tem essa permissão, não vê nomes lá e verá aqui. A divergência é deliberada: o princípio desta spec é "parceiro externo", não "quem decide".) Para quem tem partner_agency_id, cada evento sai sem autor. Ela precisa saber que o arquivo mudou; quem mexeu é operação interna da garantidora — mesma régua da nota interna e do motivo de encerramento (seção 10.6 da spec 0038). O par de → para da observação também some para ela, pelo mesmo motivo: o texto antigo é anotação de dentro da garantidora, e mostrá-lo daria a ela um histórico de observações que ela nunca teve. O evento continua aparecendo — só sem os dois valores.
RN10 — O PATCH de documento passa a registrar o antes e o depois. Hoje o payload de auditoria guarda só {document_id, filename}; passa a guardar o valor anterior e o novo de notes e de original_filename, que é o que a RN6 exige para mostrar de → para. Sem migration: a coluna é JSON.
RN10a — Fica fora de escopo a reclassificação em massa. Excluir um tipo de documento com reassign_to_id reescreve o document_type_id de todos os documentos vinculados, inclusive os de garantia fechada, sem passar pelas quatro rotas e sem gerar linha de histórico. É administração do catálogo do tenant, não edição de dossiê — e fechá-la exigiria decidir o que fazer com um tipo que não pode ser removido porque uma garantia antiga o usa.
RN11 — Nada disso alcança documento de inquilino, imóvel ou análise de crédito. A regra é sobre guarantee_id; os outros três donos seguem com as regras que já têm.
10. Autorização e isolamento¶
10.1 Matriz de papéis¶
| Papel | Pode | Não pode |
|---|---|---|
ADMIN_GLOBAL | ler garantia, dossiê e histórico de qualquer garantidora — como já lê hoje, inclusive a auditoria | registrar ou corrigir contrato assinado, e anexar documento em garantia (403 nomeado). Trocar, editar e excluir documento ele já não alcança hoje — responde 404, e a spec não mexe nisso. Ele administra a plataforma e não é atalho para dado de negócio do cliente (mesma fronteira da RN13 da 0039). As duas permissões novas não entram no conjunto dele |
ADMIN_TENANT | as duas permissões novas, no próprio tenant | escrever em garantia de outro tenant |
OPERATOR | as duas permissões novas, no próprio tenant | idem |
ANALYST | ler o dossiê e o histórico | anexar, trocar, editar ou excluir — não tem nem as permissões de base de documento |
VIEWER | ler o dossiê e o histórico | qualquer escrita |
AGENCY_USER | ler o dossiê e o histórico sem os nomes (RN9), da própria carteira | qualquer escrita em documento de garantia, em qualquer estado (fix 0011) — e alcançar garantia de outra imobiliária |
O negativo que vira teste: ANALYST e VIEWER recebem 403 ao tentar anexar em garantia fechada; AGENCY_USER recebe GUARANTEE_NOT_EDITABLE_BY_AGENCY antes de qualquer conversa sobre dossiê; ADMIN_GLOBAL recebe 403 mesmo tendo, por construção do seed, toda permissão declarada — e é por isso que a RN3 exige que ele seja excluído explicitamente do conjunto.
10.2 Permissões¶
resource:action | Rótulo (_ACTION_NAMES) | Papéis |
|---|---|---|
guarantees:dossier_attach | Anexar em garantia fechada | ADMIN_TENANT, OPERATOR |
guarantees:dossier_amend | Alterar ou remover documento de garantia fechada | ADMIN_TENANT, OPERATOR |
O rótulo vem de _ACTION_NAMES e a descrição (os dois textos longos da Parte II) é o terceiro elemento de _PERMISSIONS. Na tela a caixa lê "Garantias — Anexar em garantia fechada", porque o recurso também é traduzido.
As duas são checadas no serviço, não no Depends do router — e isso tem consequência que precisa estar escrita: a checagem depende do estado da garantia, que o router não conhece. O meta-teste test_toda_permissao_definida_e_exigida varre só app/api/v1/routers/*.py e acusaria as duas como órfãs, deixando make test vermelho e make preflight sujo.
A saída é o helper com a mesma assinatura: o serviço chama exigir_permissao(current_user, "guarantees", "dossier_attach", code=…, message=…) — com código e mensagem próprios, porque o ForbiddenError cru sai como FORBIDDEN e o contrato exige GUARANTEE_DOSSIER_CLOSED com o estado interpolado —, e o meta-teste passa a varrer também app/services/*.py, procurando a chamada exigir_permissao( e não dois literais adjacentes — a regex atual colheria qualquer par de strings vizinhas e passaria a "satisfazer" permissões futuras por acidente. Assim a permissão continua rastreável por grep — que é o que o teste mede — sem fingir que a checagem é do porteiro do router.
As duas são exigidas de fato — não existem só no catálogo. dossier_attach é checada em POST /documents; dossier_amend em PUT /content, PATCH e DELETE. Permissão declarada e não exigida responde "sim, está protegido" sem proteger, que é o achado da spec 0035.
O ADMIN_GLOBAL é a exceção explícita. O seed monta o conjunto dele como todas as permissões ({f"{r}:{a}" for r, a, _ in _PERMISSIONS}); as duas novas precisam ser subtraídas ali, ou a fronteira plataforma × cliente cai sozinha na primeira linha de código.
O seed retira o que o código não declara mais (spec 0035), então nada a remover aqui: as duas permissões são acréscimo puro.
10.3 Isolamento por garantidora (tenant)¶
Nenhuma FK nova vem no corpo. O guarantee_id do documento é lido do próprio registro já carregado com escopo (get_with_content/get_metadata filtram por tenant e por imobiliária), e o novo GET /guarantees/{id}/dossier-history resolve a garantia pelo mesmo _get_authorized que as demais rotas de garantia — 404 para fora do escopo, nunca 403.
A consulta do histórico filtra audit_logs por entity_type == "guarantee", por entity_id da garantia e pela whitelist das cinco ações da RN6a — o entity_type importa porque DOCUMENT_UPLOADED é gravado também para inquilino, imóvel e análise. O SIGNATURE_REGISTERED que define o corte da RN8 é buscado fora da whitelist, numa consulta própria — sem ela viriam CREATED, TERMINATED e SIGNATURE_REGISTERED junto, que são eventos da garantia e não do dossiê. O tenant_id de audit_logs é anulável (ações de ADMIN_GLOBAL), então o filtro é pelo entity_id, com o tenant_id da garantia validado antes — não o contrário.
10.4 Isolamento por imobiliária¶
O AGENCY_USER alcança a garantia da própria carteira (partner_agency_id), e o histórico segue o mesmo escopo. Garantia de outra imobiliária responde 404 na rota do histórico, como já responde no GET da garantia. A supressão de nomes da RN9 é aplicada no servidor, não na tela: esconder no front deixaria o nome na resposta.
10.5 Plataforma × cliente (SaaS admin)¶
O ADMIN_GLOBAL lê — inclusive o histórico, com nomes — e não escreve. A leitura está registrada como risco aceito na seção 15: GET /api/v1/audit-logs já lhe entrega os mesmos eventos hoje, porque o filtro de tenant não se aplica a quem não tem tenant. É a mesma decisão da spec 0039 para a emissão: ele administra a plataforma; alterar o dossiê de uma garantia de um cliente é ato de negócio do cliente. A guarda vem antes de resolver o documento, para que a recusa não dependa de o registro existir.
11. Impacto por app¶
apps/api¶
app/db/rbac_seed.py— duas permissões novas em_PERMISSIONS, concedidas aADMIN_TENANTeOPERATOR, e subtraídas do conjunto doADMIN_GLOBAL.app/services/permission_service.py—_ACTION_NAMESganhadossier_attachedossier_amend(o rótulo da caixa); a descrição embaixo é o terceiro elemento de_PERMISSIONS, e é onde entram os dois textos longos da Parte II. Sem isso a tela de Perfis de Acesso mostra a caixa com o nome cru da ação (dossier_attach), e a Parte II diz que é a leitura desses dois rótulos lado a lado que sustenta conceder uma e não a outra.app/core/dependencies.py— nasce o helperexigir_permissao(current_user, recurso, acao), que levantaForbiddenErrore é o que o meta-teste passa a procurar. Hoje só existeUser.has_permission, usado solto em dois serviços.tests/test_authorization_coverage.py— o meta-teste passa a varrerapp/services/*.pyprocurando a chamada do helper.- Swagger das quatro rotas de documento e das duas de assinatura, com os códigos de erro novos — é exigência do
CLAUDE.mdda API e opreflightnão a pega em rota que já existe. app/repositories/audit_repo.py— uma leitura por entidade sem paginação, filtrada pela whitelist de ações; alist_filteredde hoje é paginada com teto de 50.app/services/document_service.py— a guarda do dossiê fechado nas quatro escritas, depois da guarda da imobiliária e antes da do contrato apontado (RN5); e o payload doPATCHcom antes/depois (RN10).app/services/guarantee_service.py— nasce o serviço de leitura do histórico, e oregister_signaturee oupdate_signatureganham a guarda da RN4a (PLATFORM_ADMIN_CANNOT_AMEND): um aponta o contrato e ativa a garantia, o outro o reaponta — os dois são ato de negócio do cliente. O que ele não ganha é guarda de anexo: o "enviar arquivo" do diálogo é umPOST /documentsda tela, seguido doPUT, e a guarda do anexo já está noPOST.app/api/v1/routers/guarantees.py—GET /guarantees/{id}/dossier-history, com Swagger.app/schemas/guarantee.py— o schema da resposta do histórico.- Sem migration, e isso não quer dizer "nada a fazer no deploy". As duas permissões são dado de seed: entram pelo
make seedque o/shiproda sempre. Sem esse passo, o código novo sobe e a permissão não existe — o gestor toma 403 e a tela esconde o botão, que é a falha silenciosa que a spec 0038 pagou.
apps/web¶
components/shared/DocumentsPanel.tsx— hojeblockedReasoné um prop que faz três coisas de uma vez: mostra a frase, esconde "Adicionar" e esconde substituir/excluir. Esta spec precisa das três separadas — frase com botões, e duas permissões independentes —, então o prop se divide. É onde oAGENCY_USERpode regredir: ela depende dos três efeitos juntos, e há três consumidores do prop —credit-analyses/show.tsx(a janela da imobiliária na análise, do fix 0011) e os dois painéis da garantia. O da análise não muda de regra, mas muda de assinatura, e é onde a regressão passa despercebida.pages/guarantees/show.tsx— ocan()desta tela devolvetruepara oADMIN_GLOBAL(isAdminGlobal || …), então os controles precisam de condição própria: ele lê, não altera, e oferecer o botão seria convidá-lo a tomar 403. Vale para os dois botões que já existem — "Registrar contrato assinado" e "Corrigir" —, que hoje funcionam para ele e passam a devolver 403 pela RN4a. Passa o estado do dossiê e as permissões ao painel, e monta a seção nova; no mobile, mais umaMobileDetailSectiondepois de Documentos.pages/guarantees/components/— o componente do histórico do dossiê, extraído doHistoryTimelineda análise (é extração, não import: o de hoje é tipado aos eventos dela).pages/guarantees/components/SignatureDialog.tsx— o caminho "enviar arquivo" desabilitado com o motivo, e o texto do beco sem anexo.data-testidem tudo que é novo: a linha de estado, a seção, cada linha do histórico e o botão de tentar de novo.
apps/e2e¶
- Spec nova cobrindo: quem não tem permissão não vê os controles e recebe 403 pela API; quem tem anexa e a linha aparece no histórico; a imobiliária lê o histórico sem nomes; e 375px — seção colapsada com contagem, sem rolagem horizontal.
- E os dois casos que a divisão do
blockedReasonpõe em risco: a imobiliária no painel da análise de crédito fora da janela dela (regra do fix 0011, que não muda e não pode quebrar); e a imobiliária abrindo o painel de documentos de uma garantia e não vendo "Adicionar", substituir, excluir, lápis nem "+ observação". É regressão de tela, e o teste de API do fix 0011 não a alcança. - Sem seed novo: o mundo é montado pela API, como nas specs 0038 e 0039.
12. Contrato de API¶
As quatro escritas de documento não mudam de forma — mudam de resposta:
| Rota | Novo erro |
|---|---|
POST /api/v1/documents (entity_type=guarantee) | 403 GUARANTEE_DOSSIER_CLOSED sem guarantees:dossier_attach · 403 PLATFORM_ADMIN_CANNOT_AMEND para o ADMIN_GLOBAL |
PUT /api/v1/documents/{id}/content | 403 GUARANTEE_DOSSIER_CLOSED sem guarantees:dossier_amend |
PATCH /api/v1/documents/{id} | idem |
DELETE /api/v1/documents/{id} | idem |
POST /api/v1/guarantees/{id}/signature | 403 PLATFORM_ADMIN_CANNOT_AMEND para o ADMIN_GLOBAL — não muda para mais ninguém |
PUT /api/v1/guarantees/{id}/signature | idem |
O POST /documents também recusa o ADMIN_GLOBAL com 403 PLATFORM_ADMIN_CANNOT_AMEND, logo depois de resolver a entidade no escopo (RN5) — as outras três rotas de documento seguem respondendo 404 para ele, como hoje (RN4a). A mensagem: "A alteração do dossiê é ato da garantidora. O administrador da plataforma não altera documento de garantia de cliente."
A mensagem é um molde com o estado efetivo interpolado (vigente · vencida · encerrada) e o verbo da ação recusada: "A garantia está {estado} e o dossiê está fechado. Você não tem permissão para {anexar arquivos | alterar os arquivos} — peça a um gestor da garantidora." É este o texto, também na tela: a Parte II descreve o mesmo conteúdo em prosa, e onde as duas divergirem vale o molde daqui.
O histórico do dossiê
Os valores de action são estes sete, e não os de audit_logs — a resposta fala a língua da tela, não a do log:
action | De onde vem |
|---|---|
DOCUMENT_ATTACHED | DOCUMENT_UPLOADED |
DOCUMENT_REPLACED | DOCUMENT_REPLACED |
DOCUMENT_DELETED | DOCUMENT_DELETED |
DOCUMENT_NOTE_EDITED | DOCUMENT_UPDATED com notes alterada |
DOCUMENT_RENAMED | DOCUMENT_UPDATED com original_filename alterado |
SIGNED_CONTRACT_CORRECTED | SIGNATURE_CORRECTED |
DOCUMENT_CHANGED | DOCUMENT_UPDATED anterior a esta spec, sem antes/depois no payload |
GET /api/v1/guarantees/{id}/dossier-history Permissão: guarantees:read
200 {
// `false` **só** quando o dossiê está fechado e não há `SIGNATURE_REGISTERED`: a lista sai
// inteira e a tela mostra a linha explicando por quê (RN8). Em `PENDING_SIGNATURE` vem `true`
// com `items` vazio — o corte existe, e é o próprio fato de a garantia não ter passado a
// valer. O web não infere nada disto de `signed_at`.
"truncated_at_activation": true,
"items": [
{ "id": "uuid:campo", // o id do `audit_log` mais o campo, porque um `PATCH` que muda
// observação e nome vira **duas** linhas a partir de uma só
"action": "DOCUMENT_ATTACHED",
"document_id": "uuid", "filename": "contrato-aditivo.pdf",
"previous_value": null, "new_value": null,
"actor": { "id": "uuid", "name": "Marcos" }, // null para AGENCY_USER (RN9)
"at": "2026-08-19T14:32:00Z" },
{ "action": "DOCUMENT_RENAMED",
"document_id": "uuid", "filename": "comprovante-2024.pdf",
"previous_value": "comprovante.pdf", "new_value": "comprovante-2024.pdf",
"actor": { "id": "uuid", "name": "Ana" },
"at": "2026-08-18T09:13:00Z" }
]
}
| Erro | Quando |
|---|---|
404 | garantia de outro tenant, de outra imobiliária, ou inexistente |
403 | sem guarantees:read |
O document_id pode apontar para documento já excluído — a linha existe justamente para dizer que ele foi. Nesse caso a linha não é clicável e o nome do arquivo aparece esmaecido; sem isso, a única linha que importa no sinistro levaria a um 404.
Os campos são em inglês, como o resto dos schemas — e
previous_value/new_valueem vez defrom/to, porquefromé palavra reservada em Python e obrigaria um alias no Pydantic.
O filename vem do documento, não do payload. O log de SIGNATURE_CORRECTED guarda só document_id e signed_at; o nome sai de um join com documents, que continua encontrando o registro mesmo excluído — a exclusão é lógica e preserva a linha. Vale para todos os eventos: o nome exibido é o do documento hoje, exceto no DOCUMENT_RENAMED, onde o par de → para é do payload.
Lista completa, sem teto e sem paginação: o volume é a própria exceção (RN6), e uma garantia com trinta eventos é informação, não página. Sem teto, de propósito — como a Parte II decidiu: um teto silencioso mostraria 200 de 250 e afirmaria que é tudo, numa lista cujo propósito é ser prova.
13. Critérios de aceite¶
-
[x] CA1. Com a garantia em
PENDING_SIGNATURE, anexar, trocar, editar e excluir seguem funcionando para quem tem as permissões de base — nada mudou ali.O sujeito dos negativos é um perfil customizado, e isso não é detalhe de teste. Depois da 10.2, os dois papéis que escrevem documento no dia a dia da garantidora —
ADMIN_TENANTeOPERATOR— têm também as duas permissões novas; oAGENCY_USERtem as de base e não terá as novas, mas é barrado antes (CA7);ANALYSTeVIEWERnão têm as de base e tomam o 403 genérico do porteiro antes de chegar ao serviço. Então os CA2–CA4 se exercem com um papel criado no teste — com as permissões de base e sem a nova —, como o fix 0011 fez para provar que a proteção doDELETEera da regra e não da matriz de acesso. -
[x] CA2. Com a garantia
ACTIVE,EXPIREDouTERMINATED, um perfil comdocuments:createe semguarantees:dossier_attachrecebe403 GUARANTEE_DOSSIER_CLOSEDao anexar. - [x] CA3. Nos mesmos estados, um perfil com
documents:update/deletee semguarantees:dossier_amendrecebe o mesmo 403 ao trocar o conteúdo, ao editar observação/nome e ao excluir. - [x] CA4. As duas permissões são independentes: um perfil com
dossier_attache semdossier_amendanexa (201) e não exclui (403). - [x] CA5. O
ADMIN_GLOBALrecebe403 PLATFORM_ADMIN_CANNOT_AMENDao anexar documento em garantia e nas duas rotas de assinatura (registrar e corrigir), em qualquer tenant e em qualquer estado da garantia, mesmo tendo todas as permissões — e o teste usa a fixture que as concede todas. - [x] CA6. Nas outras três escritas de documento o
ADMIN_GLOBALcontinua recebendo 404 — o registro não é alcançável por quem não tem tenant, e o critério existe para que ninguém "conserte" isso trocando por 403 e dando leitura de tabela alheia no caminho. - [x] CA7. O
AGENCY_USERcontinua recebendoGUARANTEE_NOT_EDITABLE_BY_AGENCY, e não a mensagem de dossiê fechado — a ordem das guardas da RN5 é verificável pelo código do erro. NoDELETEo sujeito é um perfil customizado comdocuments:delete, como no fix 0011: oAGENCY_USERdo seed não tem essa permissão e tomaria o 403 do porteiro antes. - [x] CA8. O contrato apontado continua recusando troca e exclusão com
SIGNED_DOCUMENT_IN_USEem vigente e vencida, mesmo para quem temdossier_amend; na encerrada, ele passa a exigir a permissão como qualquer outro. - [x] CA9. Cada uma das seis alterações gera uma linha no histórico, com autor, data e — nos dois eventos de texto — o par anterior/novo.
- [x] CA10. Um
DOCUMENT_UPDATEDgravado antes desta spec, sem antes/depois no payload, aparece comoDOCUMENT_CHANGED("Documento alterado"), sem o par — e não some da lista. - [x] CA11. Alteração feita antes de a garantia passar a valer não aparece no histórico.
- [x] CA12. Numa garantia fechada sem
SIGNATURE_REGISTERED— as anteriores à spec 0038 e a encerrada direto dePENDING_SIGNATURE— a lista sai inteira etruncated_at_activationvemfalse. - [x] CA13. O
AGENCY_USERlê o histórico da própria carteira sem os nomes, e recebe 404 no histórico de garantia de outra imobiliária. - [x] CA14. O
ANALYST, que não temaudit:list, vê o histórico do dossiê. - [x] CA15. Na tela, quem não tem permissão não vê "Adicionar", substituir, excluir, lápis, "+ observação" nem "Anexar outra via"; e lê a linha de estado com o estado real da garantia.
- [x] CA16. Em 375px, a seção "Histórico do dossiê" aparece depois de Documentos, fechada, com a contagem no título, e a tela não rola na horizontal.
- [x] CA17. Garantia sem alteração nenhuma não mostra a seção — nos dois viewports.
- [x] CA18. O diálogo de corrigir contrato assinado desabilita "enviar arquivo" para quem não tem
dossier_attach, com o motivo; e, sem nenhum anexo, diz o que fazer. - [x] CA19. Documento de inquilino, imóvel e análise de crédito não é afetado por nada disto.
- [x] CA20. A permissão nova não dispensa a de base: um perfil com
guarantees:dossier_attache semdocuments:createcontinua recebendo 403 do porteiro ao anexar. - [x] CA20a. A tela da garantia não oferece ao
ADMIN_GLOBALos botões de registrar e corrigir contrato assinado, nem os controles de documento — ele lê, e a tela não o convida a tomar 403. - [x] CA21. As telas de
/app/admin/*não mudam: o SaaS admin não ganha painel de dossiê nem ação nenhuma sobre documento. - [x] CA22. O histórico de garantia de outra garantidora responde 404 — e o de outra imobiliária também (CA13), pelo mesmo caminho.
- [x] CA23. O
ADMIN_GLOBALlê o histórico de qualquer garantidora, com os nomes; e recebe 403 nas escritas que a RN4a nomeia (CA5). - [x] CA24. Anexar com
entity_idde garantia de outra garantidora continua respondendo 404, e nunca o 403 novo — a guarda do dossiê roda depois da resolução no escopo.
14. Plano de implementação¶
apps/api— permissões no seed (com a subtração doADMIN_GLOBAL), guarda do dossiê nas quatro escritas, payload doPATCHcom antes/depois, serviço e rota do histórico, Swagger e docstrings. Testes por RN, com os negativos de papel da 10.1.apps/web— linha de estado e condições noDocumentsPanel, seção do histórico extraída doHistoryTimeline, ajuste doSignatureDialog, nos dois viewports.apps/e2e— o spec do fluxo, com o papel sem permissão e o com.
A ordem importa por um motivo concreto: o contrato do histórico desce da API, e a extração do componente de histórico depende do formato do evento.
15. Decisões em aberto¶
Nenhuma. As seis decisões tomadas durante a escrita estão registradas nas seções 4, 5 e 7, com data.
Riscos aceitos:
- O histórico depende do log de auditoria continuar sendo escrito. Se um caminho novo de escrita esquecer o
audit.log, ele fica invisível no histórico — e a tela dirá que nada mudou. É o preço de não criar tabela própria; o teste de cada RN cobre os seis caminhos de hoje. - O
ADMIN_GLOBALlê o histórico do dossiê de qualquer garantidora, e isso é concessão registrada (decisão de 2026-08-19). O precedente que a 10.5 cita —/api/v1/audit-logs— é fechado poraudit:list, permissão do administrador da garantidora; a rota nova abre comguarantees:read, que ele tem em qualquer tenant. Ele já enxerga os mesmos eventos pela auditoria, então a informação não é nova; a porta é. - A fronteira plataforma × cliente não é fechada por regra nas três rotas por id. Ali o
ADMIN_GLOBALé barrado por efeito colateral do filtro de tenant (404), não por decisão escrita — se algum dia alguém der a ele escopo de leitura nessas rotas, a porta reabre em silêncio. Está registrado; fechar por regra é trabalho de quem tratar a fronteira inteira. - O
ADMIN_GLOBALcontinua podendo encerrar e anotar garantia de cliente. A spec 0039 registrou isso como risco aceito, e esta estreita o que é dossiê: documento e as duas rotas de assinatura. Encerrar e anotar ficam para quem for tratar a fronteira inteira. - Não há alerta ativo. Quem não abrir a garantia não fica sabendo que o dossiê mudou. Consultável não é o mesmo que avisado, e a spec escolheu o primeiro.
16. Revisão do detalhamento¶
Preenchido pelo laço do /spec new, rubrica .claude/rubrics/detalhamento.md. Registrar todas as rodadas, inclusive as reprovadas.
| Rodada | Critical | Important | Minor | Veredicto |
|---|---|---|---|---|
| 1 | 2 | 4 | 5 | reprovada |
| 2 | 0 | 6 | 6 | reprovada |
| 3 | 1 | 4 | 5 | reprovada — laço interrompido por decisão de escopo do usuário |
| 4 | 0 | 2 | 6 | reprovada |
| 5 | 1 | 2 | 7 | reprovada |
| 6 | 0 | 5 | 5 | reprovada — laço parado no limite, com uma decisão para o usuário |
Resultado: fechada por decisão do usuário na 6ª rodada, sem carimbo de aprovação. As duas primeiras rodadas acharam defeito de desenho — a guarda do SaaS admin trancada por permissão em vez de código, e o 403 novo virando oráculo de existência —, e as duas foram corrigidas. Da quarta em diante o laço passou a devolver detalhe de redação e contradição entre seções, sem Critical: o sinal de que o custo de mais uma volta passou o do que ela ainda acha.
Pendências aceitas — o que a rodada 6 apontou e não foi fechado:
- A garantia vencida não tem caminho para corrigir o contrato apontado. Trocar e excluir batem em
SIGNED_DOCUMENT_IN_USE; reapontar bate emSIGNATURE_LOCKED, porque corrigir o registro só vale enquanto vigente (RN10 da 0038). É beco real, herdado, e a Parte II manda a tela instruir esse caminho — que ali está morto. Quem for implementar precisa saber disto: a mensagem da tela, na vencida, não pode prometer a correção. - O
ADMIN_GLOBALlê o histórico do dossiê de qualquer garantidora (seção 15), pelaguarantees:readque ele tem em todo tenant. Ele já vê os mesmos eventos pela auditoria; a porta é que é nova. - A fronteira plataforma × cliente não é fechada por regra nas três rotas de documento por id — ali ele é barrado por efeito colateral do filtro de tenant (404).
- A reclassificação em massa de tipo de documento (RN10a) atravessa o dossiê fechado sem permissão nova e sem histórico.
17. Revisão da implementação¶
Preenchido pelo /spec implement, rubrica .claude/rubrics/implementacao.md. As revisões de fase (api, web, e2e) vão nas primeiras linhas; a da branch inteira, na última.
| Rodada | Escopo | Critical | Important | Minor | Veredicto |
|---|---|---|---|---|---|
| 1 | apps/api | 0 | 5 | 5 | REPROVADO |
| 2 | apps/api | 0 | 2 | 6 | REPROVADO |
| 3 | apps/api | 0 | 1 | 5 | REPROVADO |
| 4 | apps/api | 0 | 0 | 5 | APROVADO |
| 1 | apps/web | 0 | 2 | 5 | REPROVADO |
| 2 | apps/web | 0 | 2 | 5 | REPROVADO |
| 3 | apps/web | 0 | 1 | 4 | REPROVADO |
| 4 | apps/web | 0 | 1 | 8 | REPROVADO |
| 1 | apps/e2e | 1 | 3 | 6 | REPROVADO |
| 2 | apps/e2e | 0 | 1 | 5 | REPROVADO |
| 3 | apps/e2e | 0 | 1 | 5 | REPROVADO |
| 1 | branch | 0 | 0 | 7 | APROVADO |
O apps/web gastou quatro rodadas, e é o preço de não ter suíte. Cada correção só é lida pela primeira vez pelo revisor seguinte — tsc verde diz que compila, e nada mais. Três das quatro rodadas acharam defeito que a correção anterior criou, que é a exceção que o skill prevê: a releitura da garantia no caminho de recusa (rodada 3) desmontava o diálogo que carregava a recusa (rodada 4), e o key que abria a seção no erro (rodada 3) a fechava no acerto (rodada 4). O que cada rodada cobrou:
- O SaaS admin continuava recebendo os controles de documento na garantia que aguarda assinatura (a exclusão dele estava atrás de
dossieFechado), e o parde → paradesenhava "vazio → vazio" para a imobiliária — sobre o campo que a RN9 existe para esconder dela. - O botão "Tentar de novo" apagava o bloco que o continha, e o CA20a não tinha teste em fase nenhuma.
- A releitura da garantia no caminho de recusa desmontava o
ConfirmDialogque acabara de receber a mensagem — a 7.3 pede as duas metades, e chegava só a segunda. aoAlterarestava condicionado adossieFechado, o que desligava a releitura exatamente na transição em que ela serve: quem abriu a garantia aguardando assinatura e teve a assinatura registrada por outra pessoa seguiria vendo os botões até recarregar a página.
O apps/e2e gastou três, e as três foram sobre a mesma pergunta: o teste falharia?
Critical— as oito negativas por linha de documento eram inertes: o painel desenha o cabeçalho uma viagem de rede antes das linhas, e era o cabeçalho que as ancorava. Nasceu daí oesperarALinhaDoDocumento, por onde toda negativa passa.- A ausência do lápis era verde por vazio — ele só existe em documento com observação, e nenhum documento do teste tinha. Nasceu daí o
comObservacaoEsemObservacao, que monta os dois ramos do ternário. guarantee-signature-buttonera afirmado ausente numa garantia vigente, onde a máquina de estados já o esconde de todo mundo. O CA20a virou dois testes, um por estado.
O que cada rodada da API cobrou, e o que fechou:
- Metade do histórico sem prova — o corte da RN8 e quatro dos seis eventos da RN6 sem teste que morresse com a regra; o
filenamedoSIGNED_CONTRACT_CORRECTEDsaindonull; CA6 e CA22 sem asserção. nomes_por_idconsultando sem filtro detenant_id(contorno doBaseRepository) e um bloco duplicado inalcançável na guarda.- A supressão do par
de → parapara a imobiliária (RN9) sem teste — regra de confidencialidade sustentada só por leitura. - Aprovada. Os cinco
Minorforam triados abaixo; o primeiro deles foi corrigido na mesma rodada, os outros quatro viraram pendência.
Defeitos aceitos (fase apps/api, rodada 4):
- Corrigido em vez de aceito: a guarda do dossiê falhava aberta quando a garantia não carregava. Hoje ela recusa com 404 e há teste direto no serviço, com red-green verificado (revertida a recusa, o teste falha).
DOCUMENT_RENAMEDtambém tem o parde → parasuprimido para a imobiliária. A RN9 nomeia a observação; o título dela (“sem os nomes e sem o texto anterior”) admite a leitura mais larga, e ofilenameatual continua indo. Fica como está.UUID(e.payload["document_id"])e_EVENTO_POR_ACAO[e.action]estouram se umaudit_logchegar fora do formato que os cinco caminhos de escrita gravam. Inalcançável pelo código de hoje.- Cobertura mais estreita que a regra em três pontos:
dossier_amendtestado só na garantia vigente, o SaaS admin só em dois dos quatro estados, e não há negativo de quem não temguarantees:readnoGET /dossier-history. - A subtração das duas permissões do
ADMIN_GLOBALno seed não tem teste — a trava real é a guarda de código, que tem.
18. Registro¶
A ordem foi a do plano — apps/api → apps/web → apps/e2e —, e o que mudou em relação ao planejado está abaixo.
O blockedReason não se dividiu; ganhou dois vizinhos. A seção 11 previa dividir o prop, e o risco que ela nomeava era a regressão do AGENCY_USER nos três consumidores. Dividir o prop era justamente o que criaria esse risco: blockedReason continua inteiro e com o mesmo sentido — a regra do fix 0011, que fecha os três grupos de uma vez —, e as duas permissões novas entraram como podeAnexar e podeAlterar, com default true. As três condições ficaram separadas, que era o objetivo, e o painel da análise de crédito não precisou mudar uma linha.
A extração virou dois componentes, não um. O TimelineItem era o previsto; no caminho apareceu que o par de → para também estava para ser copiado, com o mesmo sx e a mesma justificativa de 375px. Os dois moram em components/shared/Timeline.tsx, e o histórico da análise de crédito passou a consumir ambos.
Um texto que a spec não escreveu, e por que ele existe. A tabela da 7.1 dá quatro frases para a linha de estado, todas para o dossiê fechado. Faltava o caso do ADMIN_GLOBAL em PENDING_SIGNATURE: a RN4a não tem estado, então ele não recebe controle nenhum ali — e sem frase o painel escondia sem explicar, que é o oposto do que a 7.1 decidiu para todos os outros papéis. A frase é "O dossiê é da garantidora. O administrador da plataforma consulta os arquivos e não os altera.", e vale em qualquer estado.
Um desvio consciente da 7.3. Ela manda a recusa de alteração para "o alerta que o painel já tem no topo do corpo". A da exclusão passou a aparecer dentro do próprio ConfirmDialog: o alerta do topo fica atrás do backdrop, e quem confirmou veria o botão sair de "Aguarde…" e mais nada. É a mesma escolha que o diálogo de encerrar já fazia desde a spec 0038.
O sujeito dos negativos de tela teve de ser construído. Depois da 10.2 não existe papel de sistema que assine e não anexe: ADMIN_TENANT e OPERATOR receberam as duas permissões novas, e ANALYST/VIEWER não têm nem as de base. O e2e monta um usuário sem papel de sistema com um perfil customizado de onze permissões, reusado entre execuções e reescrito a cada uma — sem reescrever, mudar a lista não teria efeito enquanto ninguém apagasse o usuário à mão no tenant compartilhado.
Pendências aceitas (o que os portões apontaram e se decidiu não fazer nesta spec):
- O beco do diálogo de assinatura não tem e2e, e não é escolha: chegar a
semSaidaexigiria uma garantia vigente sem documento nenhum, e a RN4 recusa excluir o contrato apontado enquanto ela estiver vigente. É o retrato das garantias anteriores à spec 0038, que a seção 5 deixou fora de escopo. - As duas frases intermediárias da linha de estado (só
dossier_attach, sódossier_amend) não são medidas por suíte nenhuma — trocá-las entre si não deixaria rastro. A independência das duas permissões está provada na API (test_as_duas_permissoes_sao_independentes); o que falta é o texto. - O estado de erro do histórico (alerta e "tentar de novo") não tem e2e. É caminho que já regrediu duas vezes durante a implementação, e
page.routetornaria o teste barato. - O CA18 é medido só no desktop. Em 375px o controle é outro elemento — um
Buttondesabilitado no lugar doRadio—, e nada o exercita. imobiliariaDoTestepode duplicar a imobiliária na primeira execução de um ambiente limpo, com workers em paralelo. Herdado do e2e da spec 0038; não afeta asserção nenhuma.DOCUMENT_RENAMEDtambém tem o parde → parasuprimido para a imobiliária, e a RN9 nomeia só a observação. O título dela ("sem os nomes e sem o texto anterior") admite a leitura mais larga, e o nome atual do arquivo continua indo.- A linha do dossiê aparece para o
ADMIN_GLOBALtambém emPENDING_SIGNATURE, e a 7.3 diz "dossiê aberto: sem faixa". É a divergência descrita acima, deliberada. - O nome do arquivo só sai esmaecido na linha de exclusão. Um
DOCUMENT_ATTACHEDde documento apagado depois continua em cor normal — o contrato não expõe se o documento ainda existe, e a linha que a §12 nomeia (a da exclusão) está certa. - A concessão das duas permissões ao
OPERATORnão tem teste. OADMIN_TENANTé exercido pela fixture; oOPERATOR, não. Mesma família da subtração doADMIN_GLOBALno seed: a trava real é a guarda de código, que tem prova. - Os quatro riscos aceitos da seção 15 seguem valendo, sem mudança.