Pular para conteúdo

0008 — A caixa azul do passo Documentos afirma o que o sistema não faz

Status CORRIGIDO
Branch fix/0008-caixa-azul-do-passo-documentos
Aberto em 2026-08-14
Severidade MÉDIA
Apps afetados api (sem impacto) · web · e2e
Nota do diagnóstico 8,7 (tentativa 1 de 3)
Nota da correção 8,3 (tentativa 2 de 3)

Status: RASCUNHO → DIAGNOSTICADO → EM CORREÇÃO → CORRIGIDO

O fix atravessa dois portões, cada um com nota de 0 a 10 dada por um subagente verificador de contexto limpo, em até 3 tentativas, passando a partir de 7,0:

  1. O diagnóstico (seções 1 a 4) é validado na seção 5 (reprodução × causa raiz). É o portão barato: sem ele, o segundo mede o conserto contra um palpite.
  2. A correção (o diff e os testes) é validada na seção 11 (correção × regressão), com duas perguntas — atacou a causa ou o sintoma, e o que prova que não volta.

1. Sintoma

No passo Documentos da análise de crédito, a primeira coisa da tela é uma caixa azul com o título "Registro criado" e o texto:

A análise P202608-00006 já existe e aceita anexos. Cada documento é gravado contra ela.

O relato foi de que o texto "não está claro". Ao abrir o código, o problema é maior que clareza — são três defeitos empilhados na mesma caixa, e o terceiro é o grave:

  1. "gravado contra ela" é jargão traduzido. É o inglês recorded against; em português "gravado contra" soa adversativo, como se o documento pesasse contra a análise. Este é o que o relato pegou.
  2. "Registro criado" aparece sem nada ter sido criado. No screenshot o cabeçalho diz "Editar análise de crédito" e o selo diz "Em edição" — a análise P202608-00006 foi criada às 07:58 e está sendo reaberta depois. A caixa anuncia a criação de um registro que já existia.
  3. "Cada documento é gravado contra ela" é falso. Documento de inquilino, de corresponsável e de imóvel é gravado no cadastro da entidade, não na análise. Só a área Adicionais da análise grava na análise.

O terceiro é conferível no próprio screenshot, sem abrir o código: quatro linhas abaixo da caixa, na linha do CPF, o app escreve "Já no cadastro de José da Silva · 12/08/2026". A tela afirma as duas coisas ao mesmo tempo — que todo documento é gravado na análise, e que aquele documento mora no cadastro da pessoa.

O efeito prático não é estético. A caixa é a única explicação que a tela dá sobre onde o arquivo vai parar, e ela ensina o modelo errado: quem lê conclui que anexar ali é um ato local à análise, e portanto que remover o documento afeta só esta análise. Não afeta — remove do cadastro da pessoa, para todas.

2. Reprodução

  1. Entrar como usuário da garantidora com credit_analyses:update.
  2. Abrir uma análise que já existe em DRAFT — no dev-remote, P202608-00006.
  3. Editar, e ir ao passo 2 · Documentos.
  4. Ler a caixa azul no topo, e depois a linha do CPF, logo abaixo.

Observado: a caixa diz "Registro criado — a análise P202608-00006 já existe e aceita anexos. Cada documento é gravado contra ela", enquanto a linha do CPF diz "Já no cadastro de José da Silva · 12/08/2026".

  • Ambiente: dev-remote (https://dev.garantora.com.br/app), garantidora "Garantidora". A análise do relato existe no banco: P202608-00006, DRAFT, created_at = updated_at = 2026-08-14 07:58:54 — nunca editada depois da criação, e ainda assim reaberta em modo de edição no horário do screenshot.
  • Superfície: ambos. A caixa é um <Alert> sem variação por breakpoint (DocumentsStep.tsx:132-139). O relato veio do mobile — o print anexado ao pedido é de um iPhone (barra de status às 07:24, aba inferior "Análise de Crédito" visível), e é ali que a caixa pesa mais, por ocupar o topo de uma tela de 812px. A verificação visual do implement deve começar por esse viewport. Não é agravado pelo PWA — é texto literal no bundle, não dado cacheado.
  • Dados/pré-condições: basta a matriz do tenant exigir algum documento numa área de pessoa ou imóvel, que é o caso normal. Com exigência só em Adicionais, a frase seria verdadeira e o defeito 3 não apareceria.

3. Esperado × Atual

  • Esperado: a caixa explica onde o arquivo vai parar e não contradiz a linha logo abaixo; e não anuncia criação quando nada foi criado.
  • Atual: anuncia criação incondicionalmente, e afirma que todo documento é gravado na análise — o oposto do que o código faz nas três áreas de pessoa e na do imóvel.

4. Causa raiz

Onde

  • apps/web/src/pages/credit-analyses/components/DocumentsStep.tsx:132-139 — o <Alert> é renderizado sem condição nenhuma, com título e frase literais. Não há ramo para "acabei de criar" × "estou editando", nem para a área do bloco.
  • apps/web/src/pages/credit-analyses/components/DocumentsStep.tsx:45-53 — blockEntity() é o código que decide o destino do anexo, e o comentário dele diz, com todas as letras: "A entidade que recebe o anexo — deriva do bloco, não da análise". renter_id → renter; property_id → property; só o resto → credit_analysis. A caixa afirma exatamente o contrário do que está documentado nove linhas acima dela.
  • apps/web/src/pages/credit-analyses/CreditAnalysisForm.tsx:429 — if (a.status === "PENDING") setStep(2). Uma análise devolvida abre direto no passo 2, e vê "Registro criado" sem ter passado por criação nenhuma nesta sessão.

Não é regressão, e a origem explica o texto. O git log -S"gravado contra ela" aponta uma única entrada: 6455b20 feat(web,e2e): telas de análise de crédito no design system (spec 0015). A frase nasceu junto com a tela, quando a distinção entre "documento da análise" e "documento da pessoa" ainda não existia — ela só passou a existir com a spec 0008 e a conferência por pessoa (RN8). O texto envelheceu; ninguém o releu.

O mecanismo

A caixa foi escrita para responder a uma dúvida real e específica do passo 2: "eu ainda não terminei o cadastro, posso anexar?". A resposta correta é sim, porque a análise já foi gravada ao sair do passo 1 (CreditAnalysisForm.tsx:547) — e é isso que "já existe e aceita anexos" diz bem.

O defeito veio de continuar a frase. Para justificar o "aceita anexos", o texto acrescentou onde o anexo é gravado — e escolheu a análise, que é o assunto da tela, sem consultar blockEntity, que é quem decide. A informação certa (a análise já foi salva, pode anexar) ficou grudada numa informação errada (e tudo fica gravado nela), e as duas chegam ao usuário como uma coisa só.

Os outros dois defeitos vêm da mesma origem: a frase foi escrita para um momento — o instante seguinte a criar a análise — e depois o componente passou a ser exibido em outros três (edição de rascunho, retorno de pendência, reabertura). Como o <Alert> não tem condição, o texto de um momento passou a narrar todos.

Por isso o bug aparece aqui e não em toda parte: é o único ponto do app que descreve por escrito o destino de um anexo. O DocumentsPanel das outras telas anexa sem explicar, e por isso não tem como se contradizer.

O alcance

  • A caixa é uma só (DocumentsStep), e só o passo 2 do formulário a mostra. Greppei "aceita anexos", "Registro criado" e "gravado contra" no apps/web/src: as três ocorrências estão nas mesmas duas linhas. Não há cópia da frase em outro componente.
  • Mas a mesma afirmação errada aparece uma segunda vez, no docstring do componente (DocumentsStep.tsx:23): "Passo 2 da inclusão: anexar, já contra o código do registro criado (spec 0015, RN9)". Não é texto de usuário, e por isso não entra no sintoma — mas é a mesma causa, da mesma origem (0015), e o mais revelador é que o parágrafo seguinte do próprio docstring (:31-34) diz o certo: "Cada documento vai para a entidade que o possui, não para a análise… só a área ADDITIONAL grava contra a análise". O arquivo se contradiz internamente há uma spec inteira, e o conserto tem de alcançar os dois pontos — senão fica um comentário ensinando o modelo errado a quem mexer nisso depois.
  • Os três momentos que a caixa narra são quatro entradas no mesmo componente, e as três chamadas que levam ao passo 2 estão todas em CreditAnalysisForm.tsx: :547 (criação, ao salvar o passo 1 e avançar), :562 (mesma coisa numa análise já existente) e :324 (análise devolvida em PENDING, que abre direto no passo 2 pela RN3 da spec 0036). A quarta é a reabertura manual de um rascunho, que cai no :562. Só a primeira justifica "Registro criado" — e é isso que a condição da correção precisa distinguir, em vez de tratar só o PENDING.
  • Quem lê inclui o AGENCY_USER. A imobiliária tem credit_analyses:create e update (rbac_seed.py:227-229) e é ela quem mais anexa documento — é o público que mais depende de a caixa dizer a verdade sobre onde o arquivo fica, e o que tem menos contexto para desconfiar.
  • O fix 0007 tornou a contradição visível. Antes, a linha do documento dizia só "Anexado em DD/MM/AAAA", que não conflitava com nada. Agora ela diz "Já no cadastro de X", e as duas frases aparecem juntas na mesma tela. O 0007 não causou este defeito — ele estava lá desde a 0015 —, mas tirou dele o disfarce.
  • AREA_LABELS já dá o vocabulário certo (DocumentsStep.tsx:37-42): "Inquilino (pessoa física)", "Imóvel", "Adicionais da análise". O cabeçalho de cada bloco já nomeia a entidade dona; é a caixa do topo que destoa.

5. Validação do diagnóstico

Preenchido pelo laço de validação do /fix new (subagente fix-diagnosis-validator). Registrar todas as tentativas, inclusive as reprovadas.

Tentativa Reprodução Causa raiz Final Veredicto
1 8,7 9,0 8,7 APROVADO

Resultado: aprovado na tentativa 1 com nota 8,7 (mínimo: 7,0).

Pendências aceitas: —

Aprovado de primeira, e mesmo assim a lista rendeu: o validador achou que a mesma afirmação errada está no docstring do componente (:23), contradizendo outro parágrafo do próprio docstring (:31-34). Os greps da seção 4 não pegaram porque a redação lá é outra. Também pediu as três arquivo:linha que levam ao passo 2, em vez de só a do PENDING — é o que permite ao portão da correção conferir se a condição nova cobre todos os caminhos. As duas coisas foram incorporadas antes de fechar.

6. Correção

Preencher com /fix implement.

apps/api

  • Sem impacto. Nenhum endpoint, schema ou regra muda: o destino do anexo já está correto no código; o que mente é a frase que o descreve.

apps/web

  • Onde: components/DocumentsStep.tsx — a caixa (:132-139) e o docstring (:23), que carrega a mesma afirmação errada.
  • O quê: a caixa passa a ser instrução, não anúncio (seção 10). Deriva do checklist que o componente já recebe: quantos obrigatórios faltam, e quantos itens vêm com predates_analysis — os dois dados já estão em mãos, então não há chamada nova. O docstring :23 é corrigido junto, para parar de ensinar o modelo errado.
  • data-testid: credit-analysis-documents-step-hint na caixa — ela não tinha, e é o que sustenta as duas asserções novas do e2e.
  • Um módulo próprio, mensagemDoPasso.ts. A decisão de qual frase mostrar é ramificada (herdado × falta × completo) e o DocumentsStep já tem 250 linhas de renderização; deixá-la inline esconderia a regra dentro do JSX, que é como a frase anterior sobreviveu a uma spec inteira sem ninguém reler.
  • A prop code saiu. O único uso dela era a frase "a análise P… já existe"; o número segue visível no topo da tela, fora deste componente. Removida também do chamador (CreditAnalysisForm.tsx:1358) — não é carona: é o resto do que foi apagado.
  • Responsividade & PWA: a caixa ocupa o topo do mobile; qualquer texto novo precisa ser conferido em 375px.

apps/e2e

  • Teste de regressão: dois casos novos em procedencia-documento.spec.ts — o estado herdado (que é o do relato, e o único em que a contradição fica visível na mesma tela) e o estado sem herança. O segundo existe para o primeiro valer: sem ele, "Recuperamos…" passaria mesmo se a caixa dissesse isso sempre — que é exatamente o defeito de origem, um texto único narrando todos os momentos.

7. Risco / impacto colateral

Não toca dinheiro, regra de negócio, permissão nem dado. O risco é o inverso do usual: trocar uma frase errada por outra frase errada. A tentação é dizer "cada documento é gravado no cadastro da pessoa", que é falso para a área Adicionais da análise — o espelho exato do defeito atual.

Há também o risco de apagar a informação útil junto com a errada: "a análise já existe e aceita anexos" responde a uma dúvida real de quem está no meio de um cadastro, e some se a caixa for simplesmente removida.

8. Critérios de aceite

  • [x] CA1. Seguindo os passos da seção 2, a caixa não afirma mais que todo documento é gravado na análise, e não contradiz a linha "Já no cadastro de …" logo abaixo.
  • [x] CA2. O texto continua respondendo à dúvida que a caixa existe para responder — que a análise já foi gravada e aceita anexos agora — em todo estado em que há o que anexar, herdado ou não. Onde não falta obrigatório, a pergunta não se coloca e a frase não aparece. O CA foi reescrito assim depois da primeira rodada do portão, que mostrou que a frase tinha sobrevivido em um estado só.
  • [x] CA3. Abrindo uma análise que já existia (edição de rascunho ou retorno de pendência), a tela não anuncia criação de registro.
  • [x] CA4. A área Adicionais da análise continua descrita corretamente — o que é gravado na análise segue sendo apresentado como tal.
  • [x] CA5. Sem jargão traduzido: nenhuma ocorrência de "gravado contra".
  • [x] CA6. Em 375px a caixa não estoura nem gera rolagem horizontal.

9. Plano

  • [x] Reproduzir o bug — pelo screenshot do relato e pelo dado: P202608-00006 existe em DRAFT com created_at = updated_at, e a caixa dizia "Registro criado".
  • [x] Corrigir a causa raiz — web (a caixa, o docstring e a prop morta) e depois e2e
  • [x] Teste de regressão que falharia se a correção fosse revertida — as três frases do defeito estão em not.toContainText: Registro criado, gravado contra, Cada documento.
  • [x] make preflight — limpo
  • [x] make test — 367 passaram, 1 falhou, a intermitente já registrada. Evidência: esta branch não toca a API (git diff main -- apps/api vazio) e o teste passou isolado 3 de 3.
  • [x] make lint — tsc --noEmit do web limpo; a API não foi tocada.
  • [x] Validar no browser em desktop e mobile — screenshot dos dois estados nos dois viewports, contra o dev-remote reconstruído: overflow = 0, zero erro de console.
  • [x] e2e executado — 8 casos, todos verdes (31,7s), sendo 4 do fix 0008.
  • [x] Regra de origem unificada com a linha do documento (possessivoDoCadastro), e os 8 casos rodados de novo depois disso — todos verdes.

10. Decisões em aberto

  • [x] O que a caixa deve dizer, e se deve continuar existindo. Decidido pelo usuário em 2026-08-14: a caixa continua, e deixa de anunciar criação para dizer o que fazer. São dois estados:

    1. Sem documento herdado — a caixa diz que agora é hora de anexar os documentos obrigatórios.
    2. Com documento herdado — a caixa informa que alguns documentos já foram recuperados do cadastro do inquilino, e pede que sejam validados.

    A decisão resolve os três defeitos de uma vez, e não por acaso: ao trocar "o que aconteceu" (registro criado, documento gravado em X) por "o que fazer agora", os dois pontos em que a caixa mentia deixam de ser afirmados. E o segundo estado transforma o achado do fix 0007 em instrução — o documento herdado não é só identificado, é entregue com uma tarefa: conferir.

    Duas extensões que a decisão não menciona e que a implementação assume, pelo texto e não pela adivinhação — registradas aqui para serem contestadas se estiverem erradas:

    • A recuperação não é só do inquilino. As mesmas áreas herdam do imóvel e do corresponsável (seção 4). Dizer "do inquilino" quando o documento veio do cadastro do imóvel repetiria o defeito 3 numa escala menor. A caixa nomeia a origem pelo vocabulário que a própria tela já usa nos cabeçalhos de bloco (AREA_LABELS), ou fala em "cadastros" quando houver mais de uma origem.
    • Quando tudo já está satisfeito, não há obrigatório a pedir — a caixa fica só com o pedido de conferência. É o estado do screenshot do relato, aliás: o topo da tela já dizia "Todos os documentos obrigatórios estão anexados".

11. Validação da correção

Tentativa Correção Regressão Final Veredicto
1 8,0 8,0 8,0 APROVADO (com [REGISTRO INCOMPLETO])
2 8,7 8,3 8,3 APROVADO

Resultado: aprovado na tentativa 2 com nota 8,3 (mínimo: 7,0).

A tentativa 1 aprovou com uma marca [REGISTRO INCOMPLETO] procedente: a frase útil da caixa antiga tinha sobrevivido em um dos três estados — exatamente o risco que a seção 7 deste registro havia previsto e que eu não conferi antes de chamar o portão. Também faltava prova do ramo PROPERTY, que a seção 10 declarava ter desenhado.

Depois da tentativa 2 (aprovada), a lista ainda rendeu uma correção que valia a pena: a caixa dizia "do cadastro de imóvel" enquanto a linha logo abaixo dizia "no cadastro do imóvel". Eram duas implementações da mesma decisão em arquivos vizinhos — que é literalmente como este bug nasceu. Passaram a compartilhar possessivoDoCadastro, e a enumeração de origens múltiplas ganhou vírgula.

Defeitos aceitos:

  • O ramo "Documentos obrigatórios completos" não tem teste. É o terceiro estado, e o único sem prova executada. Vale registrar que ele é o estado do screenshot do relato — mas ali havia documento herdado, então o que a tela mostrava era o ramo herdado, que está coberto. O risco é de regressão futura.
  • A contagem de obrigatórios aparece em três lugares da mesma tela (cabeçalho do formulário, esta caixa e o alerta de pendências). Nenhuma mente; unificá-las é mudança de layout que este fix não pediu.

12. Registro

O que era: uma caixa fixa dizendo "Registro criado — a análise P… já existe e aceita anexos. Cada documento é gravado contra ela."

O que virou: mensagemDoPasso(checklist), que deriva a caixa do estado e devolve três mensagens — confira o que foi recuperado, anexe o que falta, ou está completo. A frase útil da caixa antiga sobrevive junto do pedido de anexo.

O que divergiu do plano:

  • A correção ficou mais simples que o diagnóstico previa. A causa raiz apontava para uma condição que distinguisse "acabei de criar" de "estou editando", e listou as três arquivo:linha que levam ao passo 2. Ao trocar anúncio por instrução, o ramo de criação deixou de existir — e os quatro caminhos de entrada ficam certos por construção, sem condição nenhuma sobre eles.
  • Caí no risco que a seção 7 tinha registrado. Ela avisava que apagar a frase errada podia levar junto a informação útil; a primeira rodada do portão mostrou que foi exatamente o que aconteceu — "a análise já foi gravada e aceita anexos agora" ficou em um dos três estados. A frase agora acompanha o pedido de anexo, e não um estado fixo: onde se pede anexo, a dúvida existe.
  • A prova da origem "imóvel" não existia, embora a seção 10 a declarasse. O portão apontou que origemDoBloco tinha um ramo property_id que teste nenhum percorria — o cuidado estava escrito e não estava provado. Entrou um caso com exigência em PROPERTY, que afirma "do cadastro de imóvel" e que o nome do inquilino não aparece.
  • O screenshot pegou uma concordância errada. A caixa saía com "Recuperamos 1 documento … Confira se continuam válidos". O e2e afirmava só o começo da frase e passava por cima do resto; a asserção foi apertada para a frase inteira, que é o que teria pegado sozinho.
  • A prop code morreu junto. Era usada só pela frase apagada; saiu do componente e do chamador. O número da análise segue visível no topo da tela.

Capturas: dois estados × dois viewports, contra o dev-remote reconstruído — overflow = 0 e zero erro de console nos quatro. Não versionadas (são efêmeras); o CA6 está coberto por asserção no e2e, que é a prova que fica.

Ficou de fora, de propósito: a contagem de obrigatórios agora aparece em três lugares da mesma tela (o cabeçalho do formulário, esta caixa e o alerta de pendências). Nenhuma mente, e unificá-las é mudança de layout que este fix não pediu.