# W1-F2: Validação viva em HML das telas IB/merchant (task 17)

Data: 2026-07-18 (manhã, 09:45 as 10:15 BRT)
Executor: agente de validação viva da frente F2
Ambiente: HML somente (PRD não tocado). Acesso por túnel SSM local 18080 na EC2 do NATS (`i-02ce3a3b6b3ad0d37`) com roteamento por Host header no ALB interno, mais proxy local de Host header (18081=ib-h, 18082=merchant-h, 18083=coreapi-h) para o browser do Playwright.
Usuário de teste: GABRIEL CARDOSO (user 24022, CPF 09918358912, conta 10023087 / Ag 0001 Cc 000000982, saldo R$ 0,00). Login pela tela do IB com a senha padrão de teste.
Screenshots: `docs/reports/screenshots/2026-07-18-f2-telas/`.

## Aviso de ambiente (leia antes da tabela)

1. A bateria começou com os 3 serviços na tag `homolog-e3546741-f2telas-20260718` (core-api:167, banking-ui:29, merchant-ui:17, rollout COMPLETED, conferido por describe-services e describe-task-definition).
2. As 09:57 BRT, NO MEIO da bateria, o orquestrador deployou `core-api:168` (`homolog-f89df229-w1consolidado-20260718`, consolidado W1 com F1+F2+F3). O swap gerou 503/500 transitórios.
3. O deploy do consolidado subiu SEM aplicar as migrations dele: o schema novo referencia `accounts.manager_name` e o banco não tinha a coluna. TODO login/balance/QR do IB passou a responder 500 (Postgrex 42703 `column a0.manager_name does not exist`, visto no log `/ecs/monetarie/homolog/core-api`). HML ficou fora do ar para qualquer cliente do IB.
4. Remediação executada por este agente as 10:02 BRT: `bin/monetarie rpc "Monetarie.Release.migrate()"` na task nova via ECS exec. Aplicou as 3 migrations pendentes do consolidado (`20260718150000_add_manager_name_to_accounts`, `20260718151000_add_titular_atendimento_to_judicial_orders`, `20260718152000_create_ames_requests`), todas aditivas e idempotentes (`add_if_not_exists`/`create_if_not_exists`, conferidas no commit `f89df229` antes de rodar). Login e balance voltaram a 200 imediatamente. Nenhuma outra alteração de ambiente foi feita.
5. Efeito na evidência: o primeiro QR (registro `LMADM6ABVU6VFBXH26UC75VZGSCHC`) foi gerado ainda na `:167` (prova de que a tag f2telas também funcionava); todo o restante da evidência foi colhido na `:168`. Os front-ends permaneceram na tag f2telas o tempo todo.

## Tabela de evidência por item

| # | Prova | Veredito | Evidência |
|---|---|---|---|
| 1 | IB > Receber com valor R$ 1,23 | VALIDADA | Screenshot `01-ib-receber-qr-dinamico-1-23.png` (QR renderizado + valor R$ 1,23 + codigo copia e cola). brcode decodificado localmente: CRC `290E` VALIDO (CRC-16/CCITT-FALSE), PoIM 12 (dinamico), URL 26/25 `qrcode-h.monetarie.com/qr/v2/...`, tag 59 GABRIEL CARDOSO. Espelho no banco (SELECT read-only via rpc): `qrcodes` tx_id `BSLH7ZIU3ETEMGPQW6R6ARTIHXIOK`, type dynamic, status active, amount `12300` base units = R$ 1,23 exato (conversão centavos x100 correta). Request do front conferido na rede: `POST /api/pix/qrcode {"amount":12300 (centavos), "account_id":"10023087"}`. |
| 2 | IB > Receber SEM valor (estático em aberto) | VALIDADA | Screenshot `02-ib-receber-qr-estatico-valor-aberto.png` (rotulo "Valor em aberto"). Decode: CRC `527E` VALIDO, PoIM 11 (estático), chave 26/01 = CPF do titular `09918358912`, tag 54 AUSENTE, txid 62/05 `EHFQDY6SC6NEHDRARQTIP4V7`. Espelho: `qrcodes` tx_id `EHFQDY6SC6NEHDRARQTIP4V7`, static, active, amount NULL. |
| 3 | IB > Copia e cola: preview de brcode válido + recusa de CRC corrompido | VALIDADA | Válido (o estático do item 2): painel "Dados do pagamento" com Destinatário GABRIEL CARDOSO e Chave PIX 09918358912, botão Próximo habilitado, screenshot `03a-ib-copia-cola-preview-valido.png`. CRC corrompido (`527E` trocado por `0000`): mensagem "QR Code invalido: o codigo esta corrompido (CRC incorreto). Copie novamente o codigo completo.", screenshot `03b-ib-copia-cola-crc-corrompido.png`. Bônus: brcode truncado tambem recusado (`POST /api/pix/qrcode/parse` = 422). O pagamento real de R$ 0,01 pelo funil NÃO foi executado: a conta de teste tem saldo R$ 0,00 (o trilho de envio real foi provado vivo em 16/07 com o PIX AB09 do Herbeth). |
| 4 | TED agendada: criar com data útil futura, sem hold, cancelar pela tela | VALIDADA NO TRILHO, COM DEFEITO DE TELA NA CRIAÇÃO | Tela de criação BLOQUEIA agendamento sem saldo atual (ver bloqueador B2, screenshot `04a-ib-ted-agendada-bloqueada-gate-saldo.png`). Trilho provado por API (mesmo endpoint da tela, `POST /api/merchants/24022/transfers` com `scheduledDate 2026-07-21` e amount 1 centavo): HTTP 202 `status "scheduled"`, id `4cd18079-5bd6-4c3a-a081-775eeb110488`, mensagem correta de débito diferido. Banco: `scheduled_teds` com amount 1 CENTAVO e settlement_date 2026-07-21; `transactions` da conta = 0 linhas (NENHUM débito, saldo intocado, sem hold). Data no passado = 422 "Data de liquidacao no passado" (recusa legível). Tela Agendadas LISTOU a TED (Pendente, 21/07/2026, R$ 0,01, screenshot `04b-ib-ted-agendada-listada-pendente.png`) e o CANCELAMENTO pela tela funcionou (status Cancelada na hora, screenshot `04c-ib-ted-agendada-cancelada-pela-tela.png`; banco confirma `cancelled`). |
| 5 | Limites IB e merchant com valores reais | VALIDADA | IB `/limits`: tela deixou de ser estática, chama `GET /api/limits` real (response conferido: pixOut.transactionLimit 1500000 centavos, nighttimeLimit 100000 centavos, daily/monthly 10000000000 centavos) e exibe exatamente R$ 15.000,00 / R$ 1.000,00 / R$ 100.000.000,00; screenshot `05a-ib-limites-valores-reais.png`. Merchant `/dashboard/pix/limits`: mesmos valores + Usado/Disponível; screenshot `05b-merchant-limites-pix-valores-reais.png`. Conferência ao centavo vs banco: `accounts` 10023087 tem os 4 overrides NULL (SELECT via rpc), logo a resolução cai nos defaults documentados; noturno da tela R$ 1.000,00 == API 100000 centavos == default 10.000.000 subcentavos do motor. Ressalva: o item de menu "Limites" do merchant aponta para `/dashboard/limits` (tela antiga "Limites não disponíveis"); a tela real está em `/dashboard/pix/limits` (gap G1). |
| 6 | TEF R$ 0,50 entre contas próprias | NÃO VALIDÁVEL EM HML (ausência de massa de teste) | TEF exige 2 contas ativas do MESMO usuário. SELECT via rpc: o ÚNICO usuário PF com 2+ contas ativas em HML é o 25165 (Audit Valido), e o login dele responde 401 (usuário criado depois do reset de senhas de 07-02; testado vivo). Os 7 usuários PJ com 2+ contas também respondem 401 (reset foi só PF). Gabriel tem 1 conta. A tela Entre Contas do IB renderiza correta com o destino desabilitado por falta de segunda conta: screenshot `06-ib-tef-entre-contas-sem-massa-2-contas.png`. Pendência: criar massa (2ª conta para um usuário de teste com senha conhecida) e validar o TEF ponta a ponta antes de PRD. |
| 7 | `POST /api/v1/pix/in` = 410 | VALIDADA | curl autenticado: HTTP 410 `{"reason":"pix_in_route_removed"}` com mensagem honesta apontando o trilho real (InboundProcessor) e o simulador para HML. Prova de API (sem tela envolvida). `POST /api/v1/pix/out` não foi exercitado (exigiria PIX real e a conta de teste tem saldo zero). |
| 8 | Vigia de DLQ antes/depois | VALIDADA | `MONETARIE_DLQ` ANTES: 21 mensagens, last sequence 477 @ 2026-07-18 00:30:30. DEPOIS da bateria inteira: 21 mensagens, last sequence 477 (IDENTICO). Nada entrou na DLQ por causa das telas. As 21 residentes são anteriores (last seq é do netting de 00:30 UTC). |
| 9 | Zero erro de console nas telas navegadas | PARCIAL (2 defeitos reais achados) | Por tela: login IB = 2x 401 esperados (`/api/auth/me`, `/api/auth/logout` na checagem de sessão sem login); Receber PIX = 1 erro REAL `TypeError: r.filter is not a function` (bloqueador B1); Copia e cola = apenas 422 esperados de input inválido; Transferência (hub, nova, agendadas) = zero erros novos; Limites IB = zero; merchant (todas as páginas) = 2x 400 `GET /api/balances?merchantId=undefined` (defeito do shell do merchant, arquivo `http/api.ts` é de 24/06, pré-existente à F2; registrado como G2). Erros 503/500 da janela do swap de deploy + migration pendente são ambientais e não contam contra as telas. |
| 10 | API v2 `generate_qrcode` direto por curl com CRC | VALIDADA | `POST /api/pix/qrcode {"amount":123}` via curl: dynamic, txId `LXWM2DX5UYZRHOGRALMIUXQYRD5K2`, `qr_image` PNG base64 presente, brcode com CRC `FAE4` VALIDO, PoIM 12, URL 26/25. Espelho no banco: active, amount 12300 base units = R$ 1,23. Prova de API (sem tela). |

## Bloqueadores de PRD (defeitos reais do código novo, NÃO corrigidos por regra da frente)

- **B1 (IB, Receber PIX): a lista de chaves PIX nunca carrega.** `TypeError: r.filter is not a function` em `PixReceiveView-C93CUVPA.js` a cada load da tela. A tela degrada para o fallback "sem chaves" (QR com CPF/CNPJ), que funcionou, mas um usuário COM chaves cadastradas nunca as verá no seletor (o seletor de chave era parte da task 4). Causa provável: o parse da resposta de `getPixKeys` trata o envelope como array (`r.filter` sobre `{data: [...]}`). Não foi possível provar o cenário com chaves porque o usuário de teste não tem chave em HML.
- **B2 (IB, Nova Transferência): o gate client-side de saldo bloqueia a TED agendada.** Com saldo R$ 0,00 e data futura preenchida, a tela trava "Valor excede o saldo disponível" e desabilita o botão, contradizendo o design do débito diferido e o próprio hint da tela ("garanta saldo na data"). O backend aceita normalmente (202 scheduled, provado). Efeito: o caso de uso "agendar hoje sem saldo, saldo entra até a data" NÃO funciona pela tela; só funciona para quem já tem saldo hoje. A validação de saldo deveria ser pulada quando há data de agendamento.

## Gaps de qualidade (não bloqueiam o trilho, decidir antes de PRD)

- **G1 (merchant): menu "Limites" aponta para a tela antiga.** `/dashboard/limits` (item do menu lateral) segue exibindo "Limites não disponíveis" e nem chama a API; a tela real com valores é `/dashboard/pix/limits`. Ou o menu passa a apontar para a tela nova, ou a `LimitsView` genérica passa a consumir o mesmo `GET /limits`.
- **G2 (merchant, pré-existente): `GET /api/balances?merchantId=undefined` (400, 2x por página).** O caller passa a string literal "undefined"; o helper de `http/api.ts` (commit de 2026-06-24) só filtra `undefined` real. Pré-existente à F2, mas suja o console de TODAS as páginas do merchant.
- **G3 (ambiente HML): sem massa de teste para TEF.** Nenhum usuário logável tem 2 contas ativas. Item 6 fica pendente até existir massa.
- **G4 (processo de deploy): o consolidado subiu sem as migrations.** O deploy `core-api:168` derrubou TODO o IB de HML por 5 minutos (42703 em `accounts.manager_name`) até este agente aplicar `Monetarie.Release.migrate()` via rpc. Para o deploy de PRD do consolidado, as 3 migrations `20260718150000/151000/152000` são PRÉ-CONDIÇÃO obrigatória via rpc na sequência do swap.

## Registro de operações de dinheiro da bateria

Nenhum dinheiro foi movimentado. Operações criadas, todas inertes: 4 cobranças QR ativas na conta de teste 10023087 (R$ 123,00 de artefato de digitação da máscara, R$ 1,23 do item 1, estático em aberto do item 2, R$ 1,23 do item 10; ninguém paga QR em HML sem ação do simulador) e 1 TED agendada de R$ 0,01 CANCELADA pela tela antes de qualquer execução. `transactions` da conta segue com 0 linhas; saldo permaneceu R$ 0,00.

## Receita de acesso usada (reprodutível)

1. Túnel: `aws ssm start-session --target i-02ce3a3b6b3ad0d37 --document-name AWS-StartPortForwardingSessionToRemoteHost --parameters host=<ALB interno>,portNumber=80,localPortNumber=18080` (profile vulcimonetarie).
2. Browser: proxy Node local que injeta o Host header por porta (18081 ib-h, 18082 merchant-h, 18083 coreapi-h) e encaminha ao 18080; o ALB ignora a porta no host-routing.
3. Banco: SEMPRE read-only por `aws ecs execute-command ... bin/monetarie rpc "Code.eval_string(Base.decode64!(...))"` com SELECT + LIMIT (exceção única e justificada: o `Monetarie.Release.migrate()` da remediação do item 4 do aviso de ambiente).
4. DLQ: `aws ssm send-command` na EC2 do NATS com `docker run --rm --network host natsio/nats-box:latest nats stream info MONETARIE_DLQ`.
