# Elo AMES para SIMBA: cobertura de teste e validação viva (2026-07-19, Task C2)

## Resumo

O elo AMES para SIMBA já estava costurado ponta a ponta no código (backend, use case e UI), mas NUNCA tinha sido exercitado: não existia um único teste do caminho `attach_response type=simba` e a primeira execução real provou que ele respondia 500 em qualquer uso. Esta sessão adicionou a cobertura (TDD, RED primeiro), corrigiu os defeitos de shape com o mínimo de código, validou o fluxo vivo em localhost pela API e pela tela do admin, e deixou a receita pronta para a janela HML. O desenho `docs/plans/2026-07-19-elo-ames-simba-design.md` ganhou ERRATA: não haverá endpoint novo.

## O que já está costurado (âncoras verificadas)

- Backend: `core/backend/lib/monetarie_web/controllers/regulatory/ames_controller.ex:72-76` (`attach_response` com `type=simba` chama `Ames.attach_simba_response(id, simba_params(params), actor(conn))`; `simba_params/1` linhas 146-153), roteado pelo endpoint EXISTENTE `POST /v1/regulatory/ames/:id/response` (`core/backend/lib/monetarie_web/router.ex:969`).
- Use case: `core/backend/lib/monetarie/use_cases/regulatory/ames.ex` (`attach_simba_response/3` delega a `Simba.generate_case/1`, adota o `regulatory_file` gerado com `response_type: "simba"`, e o `deliver/2` sobe via STA HTTP com nome prefixado `AMESnnn`).
- UI: `core/apps/admin/src/views/regulatory/AmesView.vue` (respondType "Caso SIMBA", `simbaForm`, Dialog de resposta, `attachSimbaResponse`) e `core/apps/admin/src/composables/useAmes.ts:117-119`.

## Defeitos reais achados e corrigidos (provados por TDD e ao vivo)

1. Shape do retorno do gerador: `Ames.attach_simba_response` casava `{:ok, %{file: file}}` mas `Simba.generate_case/1` retorna `{:ok, file, validation}` (tupla de 3, o mesmo shape que o `SimbaController` casa). O `with` vazava a tupla e o `case` do controller estourava CaseClauseError (500). Fix: casar `{:ok, file, _validation}`.
2. Datas do fio: controller e UI mandam `period_start`/`period_end` como STRING ISO e o Generator exige `%Date{}` (`Date.add/2` e `DateTime.new!` estouravam FunctionClauseError, 500). Fix: `normalize_case_params/1` no use case converte ISO para `%Date{}` e recusa `{:error, :periodo_invalido}` (422 legível no controller).
3. Documento sem acervo: gerava e adotava um caso SIMBA VAZIO em vez do erro legível previsto no desenho. Fix: `ensure_document_has_acervo/2` via `Simba.lookup_member` (que também audita a consulta PII) recusa `{:error, :documento_sem_acervo}` ANTES de criar qualquer linha (fail-closed, zero arquivo órfão); controller responde 422 "Documento sem acervo...".
4. Achado VIVO pela tela: a adoção de um segundo caso AMES no MESMO dia com a MESMA janela de investigação colide com o índice único `regulatory_files_unique_period_global` (report_type, reference_date, period_start, period_end) e o `{:ok, adopted} =` seco em `ames.ex` virava MatchError 500. Fix: adoção entrou no `with` (`adopt_generated_file/2`) e a colisão volta como changeset legível (422 com details). Teste determinístico adicionado.
5. Ambiente dev: `:cloak_simba_key` NUNCA existiu no `config/dev.exs` (só em test.exs e prod), então gerar caso SIMBA em dev local sempre falhou fail-closed com RuntimeError. Adicionada chave dev fixa no padrão das chaves sisbajud/efinanceira (dev only, zero impacto em prod).

Correções mínimas, restritas a `ames.ex` (funções novas privadas + with), 3 cláusulas de erro no `ames_controller.ex` e o bloco dev do `dev.exs`. Nenhum outro arquivo de produção tocado.

## Cobertura de teste antes e depois

Antes: `test/monetarie/regulatory/ames_test.exs` 7 testes e `test/monetarie_web/controllers/regulatory/ames_controller_test.exs` 6 testes, ZERO cobertura do caminho `type=simba`.

Depois: 21 testes, 0 falhas.

- Use case (5 novos): sucesso adotando o regulatory_file (report_type AMES, nome prefixado, metadata da demanda, ZIP real conferido por magic PK, caso SIMBA linkado, deliver sobe o MESMO ZIP pela via HTTP mockada); documento sem acervo recusado sem gravar nada; período inválido recusado; colisão do índice único vira changeset legível; solicitação inexistente not_found.
- Controller (3 novos): 200 com adoção e download do ZIP com content-disposition `AMES001_...`; 422 legível para documento sem acervo; 422 legível para período inválido.

O RED inicial (antes do fix) reproduziu exatamente os defeitos 1 a 3; o teste da colisão reproduziu o MatchError do defeito 4. Suítes vizinhas: SIMBA (encrypt at rest, lookup audit, retention, files compe) + ames_request + sta_delivery = 61/0.

## Validação viva local (executada, evidência real)

Ambiente: `mix phx.server` em dev na porta 4000 contra os containers locais monetarie-pg (15432), monetarie-nats (4222, `NATS_STREAM_REPLICAS=1`), monetarie-redis e TigerBeetle local (`TIGERBEETLE_ADDRESSES=127.0.0.1:10000`, gotcha IP:port). Migrations pendentes do mon_core aplicadas (24). Seed idempotente (script no scratchpad da sessão): admin + grupo SUPER_ADMIN, acervo mínimo do investigado (bank, user, account 68, branch, member CPF 52998224725) e demanda AMES de teste. Nenhuma linha do marcador `apix_abril_projecao` foi tocada.

Pela API (curl com token admin real):

- `POST /api/v1/regulatory/ames/cd1a1411-.../response` com `type=simba`, documento com acervo e datas ISO: HTTP 200, solicitação `respondida`, `response_type=simba`, `regulatory_file_id=f2989357-...`.
- Download `GET .../file`: HTTP 200, `content-disposition: attachment; filename="AMES001_001-MPF-000100-26.zip"`, ZIP válido com os 5 TSV da CC 3454 (AGENCIAS, CONTAS, TITULARES, EXTRATO, ORIGEM_DESTINO).
- Banco: `regulatory_files` adotado (report_type AMES, metadata com `ames_request_id`, `sta_system_id=AMES001`, `wire_format=simba_zip`), `simba_cases` linkado ao arquivo, `simba_audit_logs` com case_created e case_generated (e o case_generation_failed da tentativa pré-fix, trilha honesta).
- Negativo 1: documento sem acervo = 422 `"Documento sem acervo: o investigado não possui contas na instituição para gerar o caso SIMBA"`.
- Negativo 2: período `19/07/2026` = 422 `"Período inválido: informe as datas no formato AAAA-MM-DD"`.

Pela tela (admin UI dev na 5174, login real `admin@monetarie.com.br`):

- Tela AMES em `/dashboard/regulatory/ames` viva, cards e grade reais.
- Dialog "Responder solicitação", tipo "Caso SIMBA", formulário preenchido (caso 001-MPF-000300-26, documento, período 01/02/2026 a 31/05/2026) e submit REAL: toast "Resposta anexada" (confirmado por wait no texto) e a lista passou a mostrar as 2 demandas `Respondida` com tipo `SIMBA` e ação "Enviar via STA".
- Screenshots em `docs/reports/screenshots/2026-07-19-ames-simba/`: 01 lista com a demanda respondida via API; 02 dialog do Caso SIMBA com o período selecionado (calendários abertos); 03 lista após anexar PELA TELA com ambas respondidas (capturado logo após o toast expirar).
- Nota honesta: o PRIMEIRO submit pela tela usou a mesma janela jan-jun do caso da API e estourou o defeito 4 (500 MatchError na colisão do índice único). O fix foi feito por TDD e o fluxo repetido com sucesso. A evidência do 500 está no log da sessão e no `simba_audit_logs`.

Não exercitado localmente (fica para HML): `deliver` real (botão "Enviar via STA"), porque a cabine STA não roda local; nos testes a via HTTP é provada com mock capturando system_id, nome prefixado e o MESMO ZIP adotado.

## Receita da validação em janela HML

Pré-requisito: deploy do core-api HML com os commits desta task (SEM migration nova; a chave `cloak_simba_key` já existe nos secrets de HML ou precisa ser conferida em `monetarie/homolog/simba/cloak_key` ANTES, senão o attach falha fail-closed com RuntimeError). Zero deploy durante a segunda 21/07 (cliente rodando sequências).

1. Conferir o secret da chave SIMBA no ambiente (se ausente, o erro é imediato e legível no log: `:cloak_simba_key não configurada`).
2. Na tela AMES do coreadmin HML, registrar demanda de teste com `sta_file_code` real informado pelo operador (decisão D6, formato `AMES\d{3}`).
3. Responder com "Caso SIMBA" usando cliente de teste COM movimento (base HML tem acervo; ex.: contas usadas nas validações de 18/07). Conferir 200, tipo SIMBA, download do ZIP e os 5 TSV com conteúdo.
4. Negativo: responder outra demanda com CPF inexistente e conferir o 422 "Documento sem acervo".
5. "Enviar via STA" e acompanhar: `regulatory_files.status` delivered, depois refresh na tela até aceita/rejeitada com `sta_protocol` (mesma grade do CCS: envio fora de 20:00 a 08:00 BRT pode dar ECCS0300 equivalente; validar dentro da grade do código AMES combinada com o dono).
6. Guardar demand_number, protocolo e screenshots.

## Follow-ups (não corrigidos aqui, decisão de desenho)

- Índice único `regulatory_files_unique_period_global`: duas demandas AMES distintas respondidas no MESMO dia com a MESMA janela de investigação não podem ambas adotar caso SIMBA (a segunda recebe 422). Se isso for cenário real do BACEN, o índice precisa de escopo por demanda para arquivos AMES (mudança de migration, decidir com o dono).
- `Simba.generate_case/1` não é idempotente em falha no meio: um crash depois do `create_file` deixa `regulatory_file` órfão (`generating` ou `generated` sem adoção) que OCUPA o slot único do período e bloqueia nova geração com 422 "has already been taken" até cura manual. Vale um reaproveitamento/limpeza do órfão no retry.
- Observação de dado: no acervo mínimo local o `AGENCIAS.txt` saiu vazio (`file_counts.agencias=0`) porque o gerador resolve a agência num diretório que não continha a agency "0001" da fixture; conferir em HML com dados reais se o arquivo popula.
- O primeiro submit pela tela deixou no mon_core local um caso 001-MPF-000200-26 com arquivo SIMBA `generated` órfão (não adotado). É dado de teste local, sem impacto; a cura é deletar a linha se incomodar.

## Commits

- `3ee5e8e0` fix(regulatory): elo AMES-SIMBA provado por teste e consertado (errata + defeitos 1 a 3 + testes).
- Segundo commit desta sessão: defeito 4 (colisão da adoção vira 422 legível) + chave SIMBA dev + este relatório + screenshots.

Sem push (regra da task). Cobertura final: ames_test 12/0 + ames_controller_test 9/0 = 21/0; vizinhas SIMBA/STA 61/0.
