# Handoff 2026-07-26: PIX-out (AB03 EM ABERTO), MFA ligado, extrato e o que eu errei

Sessao longa demais. Este documento existe para a proxima comecar limpa e **nao
repetir os meus erros**. Onde eu afirmei sem prova, esta escrito.

---

## 1. Estado

`main` == `origin/main` == **`9ba42932`**, working tree limpo (so os untracked
pre-existentes).

| servico | HML | PRD |
|---|---|---|
| pix-api | td **220** | td **90** |
| core-api | td **227** | td **116** |
| core-banking-ui | td **56** | td **19** |

Guard de digest MATCH em todos os retags. Suites: core **8977/0**, pix
**4617/0**, IB **269/269**.

`REQUIRE_TRANSACTION_MFA=true` nos DOIS ambientes.

---

## 2. O QUE ESTA ABERTO E DOI: PIX-out morre com AB03

**Esta e a frente numero 1.** Ultima ocorrencia: 2026-07-26 11:19, R$1,04, chave
`+5511942503269`, Santander (90400888). Rejeitado 40s depois.

### O que esta PROVADO

- A pacs.008 que enviamos as 11:19 e **estruturalmente identica** a uma que
  LIQUIDOU (25/07, Bradesco): mesmo schema `pacs.008.spi.1.15`, mesmos campos,
  mesma ordem. So mudam os valores.
- O SPI **aceitou** a mensagem: nao houve admi.002 para ela.
- A chave saiu correta, em E.164 (`<Prxy><Id>+5511942503269</Id>`) — o fix da
  madrugada funcionou.
- Catalogo do BACEN (`monetarie_spi_ref.error_codes`): AB03 = **"Timeout na
  liquidacao (SPI)"**, categoria `ABORT`, http 504, **`is_retriable: true`**.
- Historico 30 dias: 131 PIX-out, 102 liquidados, 29 rejeitados (22%).
  Santander: **0 de 4**. Bradesco: 36 de 54.

### O que NAO esta provado (e eu errei afirmando)

**Eu apontei a causa-raiz DUAS vezes e errei DUAS vezes:**

1. Disse que era a frente de 24/07 (nossa pacs.002 saindo por canal errado).
   **NAO e**: aquela e de PIX-**in** e esta resolvida — zero admi.002 de recusa
   de canal para pacs.002 desde 25/07.
2. Disse que era a chave crua no `<Prxy>`. O fix entrou, a chave saiu em E.164
   **e o PIX falhou igual**. Era necessario, nao era a causa.

**Nao sei ainda por que o recebedor nao aceita.** Nao ha defeito nosso provado
na mensagem.

### Por onde a proxima sessao deve comecar

1. **Conferir os dados do recebedor contra o DICT.** O `<Cdtr><Id>` que
   mandamos e o CPF `32189410835` (o proprio dono). A chave `+5511942503269`
   esta registrada para ESSE CPF no Santander? Se a consulta devolveu outro
   titular e nos sobrescrevemos com o nosso, o recebedor recusa. **Verificar de
   onde sai `Cdtr.Id`**: do `entry` da consulta DICT ou do que o Core mandou.
2. **Conferir `<CdtrAcct><Id><Othr><Id>`**: enviamos
   `00000000000010303760` com `<Issr>0079</Issr>` (20 digitos, zero a esquerda).
   Bradesco liquidou com `2844` / `Issr 1851`. Conferir contra o que o DICT
   devolveu, sem reformatar.
3. **Testar com uma chave de OUTRO tipo no MESMO PSP** (Santander): se CPF
   liquida e telefone nao, o problema e da chave; se nenhum liquida, e do PSP ou
   da conta.
4. AB03 e `is_retriable: true` no nosso proprio catalogo e **nao ha retry**.
   Avaliar (com cuidado: retry em money-path).

### Achado colateral, frente propria

admi.002 de 2026-07-26 09:41:04 recusando uma mensagem NOSSA:

```
RjctgPtyRsn: "Erro no processamento da ICOM"
RsnDesc:     "Schema desconhecido, nao habilitado para uso ou canal incorreto"
Ref:         RUEkBn53Mqvyk8D4sXVJKPKni8pw/jRfM
```

O `Ref` casa com o `resource_id` de uma **camt.060** (consulta de saldo) enviada
as 09:41:03. **Nao e do PIX.** Mas e uma mensagem nossa sendo recusada
diariamente e ninguem viu.

---

## 3. O que FOI corrigido nesta sessao (com prova)

### PIX por telefone nao saia da cabine (P0)

Reserva gravada em `+5511942503269`, pagamento lendo `11942503269`, 20s depois,
reserva valida por mais 29 min. **Nao era TTL** — eram duas grafias.
`Shared.Dict.PixKeyNormalizer` na FRONTEIRA do `Shared.E2eCache`. Provado vivo:
a pacs.008 de 11:19 saiu (antes nem chegava a existir).

### Recusa silenciosa da cabine (P0)

`lookup_tracked_tx -> :not_found` caia em "audit only": reserva presa em stage
2, hold aberto, IB girando sem mensagem. `terminalize_untracked_outbound/2`
encerra pelo `transaction_id` e devolve o disponivel na hora.

### MFA por operacao (ligado nos 2 ambientes)

`MonetarieWeb.Auth.MfaGuard`, porte do `FluxiqWeb.Merchant.MfaGuard`. Plug
declarativo `RequireTransactionMfa` em `create`, `create_pix_account`,
`create_pix`, `create_ted`, `dict_lookup`, `dict_lookup_self`. Codigo de **uso
unico** (divergencia deliberada: o coreproviders permite reuso na janela).
Validado vivo em PRD com o usuario real.

### "Invalid Date" no extrato

Causa: a IB manda `X-Key-Case: camelCase`, entao a API devolve `createdAt` — a
tela lia `created_at`. Acessor unico `txTimestamp`. **Eu testei a API sem esse
header e conclui "nao reproduz" — testei errado.**

### Diretorio de participantes do SPB/PIX

`spb_participants` por ISPB + sync diario da lista oficial do BACEN (via
cabine). 883 participantes, 216 diretos no SPI.

### Outros

Recebedor com CPF no PIX-in (`OwnCreditorName`); 172 legados com CPF colado ao
nome, com backup; pagador institucional na TED (`PayerIdentity`); contrato de
webhook (forma estavel + apelidos + `eventType` nos dois trilhos).

---

## 4. Regressoes que EU introduzi

Registro para nao se repetirem:

1. **"21:00" em todo o extrato.** Minha primeira correcao do Invalid Date caia
   para `date` (DATE-ONLY): `new Date("2026-07-25")` e meia-noite UTC = 21:00 do
   dia anterior em BRT. Troquei lixo visivel por hora fabricada — pior, porque
   parece certa. O backend ja avisava disso num comentario.
2. **Quebrei dois guardas estruturais** (`OutboxAtomicityTest`,
   `CabinErrorMaskingTest`) ao extrair corpos de acao para chamar o MfaGuard.
   Refeito como plug. Pegos por teste, nao chegaram a produção.
3. **Afirmei "o cache expirou (TTL 5 min)"** sem checar o relogio. A reserva
   estava viva; eram 20 segundos.

**Licao para a proxima sessao:** neste sistema, toda afirmacao de causa precisa
de medicao no ambiente real ANTES de virar frase. Duas vezes eu apresentei
hipotese como conclusao.

---

## 5. Frentes NOVAS que o dono levantou e eu NAO ataquei

1. **Comprovante de PIX recebido sem os dados do PAGADOR.** A tela mostra so
   "DADOS DO RECEBEDOR (VOCE)". Num PIX recebido, quem pagou e a informacao
   principal. Comparar campo a campo com o comprovante do coreproviders.
2. **Palavras em ingles cruas na tela.** A home mostra os chips `transfer` e
   `confirmed` (valores do banco) em vez de rotulo traduzido. Varrer o IB por
   valor cru renderizado sem i18n.

---

## 6. Fila herdada

- 6 eventos documentados que nunca sao emitidos; `pix.received` sem payload documentado.
- Campos que nenhum produtor monta (§3 do relatorio de webhooks).
- 2 chaves DICT com CPF no `owner_name` (investigar via CID sync, NUNCA GetEntry).
- `Institutions.Cache` fora da arvore de supervisao em PRD.
- 16 DLQ de HML; lag ICOM p95/p99; portar credito coreproviders + `reverse_entry`;
  conciliacao SPB x Core contra BACEN real.

---

## 7. Ferramentas e gotchas

- `rpc.sh <cluster> <servico> <container> "<bin> rpc" <arquivo.exs>` (scratchpad
  de sessao, fora do repo).
- **Chamada HTTP dentro de `Task.async` + `Task.yield` derruba a sessao do ECS
  Exec** (o `exit` propaga). Use `try/catch` ou processo desacoplado com
  `:persistent_term`, lido numa segunda chamada.
- **PRD loga so `warning`/`error`** em alguns servicos: `Logger.info` some.
- Varredura de XML em `icom_received` SEM filtro de data estoura a sessao.
- Regex do Postgres nao aceita `{0,500}` grande: use `strpos` + `substring`.
- **Testar a API do IB SEM `X-Key-Case: camelCase` da resultado diferente do
  que o browser recebe.** Foi o que me fez errar o diagnostico do extrato.
