# Handoff 2026-07-01: ETL completo do legado PIX/SPB + revalidação geral dos fluxos HML

Data: 2026-07-01 (fechamento na madrugada de 2026-07-02 UTC)
Escopo do mandato do dono: "ETL completo de todo o sistema legado conforme backup disponibilizado + revalidação geral de todos os fluxos (cabine PIX, cabine SPB e integrações com o Core), trabalho extenso e exaustivo para cobrir lacunas e potenciais erros, visto que temos ambiente real em funcionamento na homologação conectado ao BACEN". GEN0006 validada pelo dono; MES01 segue indisponível (RTM).

Estado de referência: `origin/main` avançou de `f79e17fa` até `1d375eb8` nesta sessão. Deploys HML novos: `pix-api:91`, `pix-admin-ui:11`, `spb-api:26`, `core-api:78`.

## 1. O que foi entregue

### 1.1 ETL completo do legado (commit `e768d5a7`)

Carga v2 executada e provada no laboratório local (containers `monetarie-bak-mssql` + `monetarie-postgres-1`, bancos `cc_pix`/`cc_spb`). Relatório completo com todas as conciliações: `docs/reports/2026-07-01-pix-spb-etl-completo-v2.md`. Design com o mapa oficial de status legado (comprovado em procedures T-SQL do AB_MGI e no .NET decompilado do LegadoPIX): `docs/plans/2026-07-01-etl-completo-legado-pix-spb-design.md`.

- Staging completo dos 16 backups: PIX 3.373.971 linhas/210 fontes; SPB 2.003.396/55. Exclusões com zero linhas: `CERTIFICADOS`, `PARAMETROS`, `PARAMETROS_MQ`, `PARAMETROS_SEGURANCA`, `THUMBPRINT_*` (3), `DOWNLOAD_CERTIFICADOS_SITE`, `TESTE_CONECTIVIDADE`.
- Projeções de negócio PIX: `transactions` 4.391 + 54 exceções contadas (= 4.445 ordens; nunca inventar identidade financeira), `credit_notifications` 8.761, `balance_blocks` 4.392, `transaction_returns` 488, `infraction_reports` 390+1, `refund_requests` 205, `xml_messages` +4.373. Conciliações ao centavo: settled R$ 166.875.645,67 = extrato D/SUCESSO exato; anotações R$ 159.722.913,42; bloqueios R$ 168.935.505,42.
- Correção do acervo SPB: `message_type` real via `CodMsg` do XML (LEGACY zerado nas 14.101 mensagens; 130 ops TRANS_SISTEMAS sem XML ficam como exceção declarada), direção real (7.134 in/6.967 out), estados canônicos (`legacy_imported` zerado), valores via `value_tag` (R$ 195,1M em mensagens), `operation_id` backfill 14.101, 10.591 eventos de timeline. Dois defeitos da 1ª carga reparados (5 pares NUMCTRLSPB duplicados; `operation_id` nunca casava).
- Gate `--allow-balance-overwrite` protege o upsert destrutivo de `cecresa_spi.balances`. Idempotência provada: 2ª rodada com delta zero nas 22 tabelas destino.
- A promoção para HML continua sendo pacote separado (seção 7 do design): retarget `cecresa_*`→`monetarie_*` + Aurora + decisões de exibição. NÃO executar sem aprovação do dono.

### 1.2 Defeitos de código diagnosticados e corrigidos (commits `cb844b73`, `6fd37785`, `79f1f0ac`, `0a897710`, `1d375eb8`)

Todos com causa-raiz comprovada no banco vivo + código, revisão adversarial dupla e testes:

1. **Cinco mapas divergentes de `messages.status_id` no PIX** (`cb844b73`). A ref table viva fora semeada sem ids explícitos (ordem da sequence: 1=ACSP...8=RCVD) enquanto o runtime grava 1=PDNG...4=ACSC...8=RJCT. Consequências reais: o relatório APIX001 filtrava liquidada por 7 e contava ZERO; a Central de Mensagens exibia liquidadas como "Rejeitado". Fonte única agora: `Shared.Spi.StatusCodes` + migração `20260701090000` re-seedando `monetarie_spi_ref.status_codes`/`operation_status_codes` com ids explícitos (aplicada e conferida no banco vivo) + teste de contrato módulo↔banco. Nunca rotular `status_id` por join com a ref table em análises antigas ao histórico desta data.
2. **admi.002 do BACEN sem correlação** (`6fd37785`): rejeições cujo Ref (BizMsgIdr) não estava em `messages` caíam em "sem transação correlacionada" e a pacs.008 ficava em ACSP para sempre. Novo hop Ref→`bacen_outbound`→E2E + `StuckOutboundChecker` (WARNING agregado a cada 10min para outbound não-terminal >30min; nunca altera status).
3. **`refund_requests` com schema duplo** (`79f1f0ac`): schema órfão incompatível deletado; canônico = `DictService.FundsRecovery.RefundRequest`; validação E2E alpha-aware portada.
4. **SPB LPI0006 sem handler** (`0a897710`): notificação diária do STR (transferência automática da Conta PI) caía em `:unknown_response_type`→NAK→linha eterna em `processing`. Base inbound não-solicitada com `direction_flag=R` agora vira `:inbound_notification` (confirmed+ACK). Erro permanente vira TERM+`mark_failed` somente com catálogo ETS carregado (vazio segue NAK e o `MessageTypeDriver` re-tenta a cada 5s). `StaleInboundSweeper` novo; `StaleOperationSweeper` preenche `error_code`/`failed_at`. Semântica corrigida no `LpiHandler`.
5. **Core `PixConsumer` crashava com `transaction_id` inteiro da cabine** (`1d375eb8`, descoberto na validação viva desta sessão): a cabine publica o id interno inteiro; `get_by_transaction_id` estourava `Ecto.Query.CastError` (crash real em 2026-07-02 00:28 UTC consumindo a rejeição da tx 100; eventos das txs 101-103 perdidos no restart). Normalização inteiro→string no padrão do `OutboundRequests`; regressão coberta com o payload real. Deployado em `core-api:78`.

### 1.3 Operações de dados em HML (executadas ao vivo, com trilha)

Nas tasks vivas (`pix-api:91`, `spb-api:26`), tudo com evidência BACEN documentada em history/eventos:

- **PIX, txs 100-103** (rejeições do BACEN nunca aplicadas por falha de correlação): rejeitadas pelo caminho oficial (evento STATUS_UPDATED, StatusUpdater aplicou RJCT + completion_time + history origem BACEN citando os admi.002) + liberação dos 4 holds de R$ 0,01.
- **PIX, txs 104/133/134** (falsos settled de builds pré-`92844536`): corrigidas para RJCT com evidência admi.002 (gate prévio: zero pacs.002 citando os e2e), settlement_time anulado, 3 `netting_transactions` espúrias deletadas (ciclo 3499 fechado sem settled_at, positions=0) e `accounting_events` neutralizados.
- **SPB, 4 LPI0006**: reinjetadas via NATS e processadas pelo handler novo AO VIVO (confirmed + linha normalizada, sem dupla contagem).
- **SPB, backfill**: 7 operações `error` receberam `error_code='STALE_NO_MQ_ACK'` + descrição + `failed_at`.
- **SPB, MQRAW 29c8a5e9**: NÃO decifra e é terminal documentado — a mensagem de 06-30 14:51 UTC foi cifrada para o certificado SPB ANTERIOR (serial 7B9AB4FA...), cuja chave privada não existe em nenhum UID do vHSM; o HSM responde 500. Binário íntegro preservado. Nada a fazer sem a chave antiga.
- **Core**: baseline de corte 2026-07-01 confirmada (zero correlatos das txs corrigidas no mon_core pós-reset; nada a reconciliar retroativamente). Crons de reconciliação vivos (<30min).

### 1.4 Validação viva fim-a-fim (regra #11) — resultado parcial com achado novo

Dois PIX Out reais de R$ 0,01 disparados pelo IB em HML conectado ao BACEN (01:37Z e 01:55Z de 2026-07-02; comprovante do IB exibiu "PIX solicitado — em processamento" com ids `PIXOUT-930838e5a6e4d908` e `PIXOUT-edd1ed546768d208`). Evidência do pacs.002 em `docs/reports/screenshots/2026-07-01-postfix-ib-pix-out/`.

O que FOI provado ao vivo:
- A cabine processou os dois envios ponta a ponta com o BACEN (messages 232/233, pacs.008 assinadas via HSM, canal ICOM) e AMBOS receberam pacs.002 RJCT AB03 (timeout do recebedor homolog, >40s sem resposta — mesmo comportamento observado em 06-28; o recebedor de teste do BACEN homolog é instável).
- As DUAS rejeições foram aplicadas AUTOMATICAMENTE pela cabine (status 8 + completion_time em segundos), validando ao vivo o caminho de rejeição pós-correções desta sessão.

O que NÃO foi provado (achado novo, prioridade P0 na revisão de paridade):
- O Core NÃO tem NENHUM registro dos dois envios: `transactions` = 0, `outbound_requests` = 0 (por PIXOUT id, por e2e e por janela de tempo), e o log do core-api está silencioso na janela (nenhum POST pix, nenhum erro — atenção: o logging de request em prod pode estar mínimo, então ausência de log não é conclusiva). Consequências: a materialização PIX no Core segue sem prova viva, o IB não teria como exibir comprovante/status dessas transações depois, e a rota exata IB→(core?/gateway?)→cabine precisa de diagnóstico sistemático. O runner do E2E morreu por falha de infraestrutura da API antes de fechar o JSON de validação; o primeiro envio coincidiu com a janela do rolling deploy do core-api :78, mas o segundo foi depois, então o deploy não explica sozinho.
- O fix do CastError (`1d375eb8`) foi validado por teste de regressão com o payload real do crash, mas ainda não por consumo vivo (não houve evento consumível: sem registro no Core, o PixConsumer não tinha o que materializar).

## 2. Follow-ups abertos (ordem de prioridade)

0. **P0 — PIX Out pelo IB sem registro no Core**: os 2 envios vivos de 2026-07-02 (seção 1.4) geraram comprovante "em processamento" com id PIXOUT e foram processados pela cabine, mas `transactions`/`outbound_requests` do mon_core ficaram vazios. Diagnosticar a rota real do envio do IB (controller v2 do Core? gateway/proxy da cabine? provider dispatcher por entidade?) e onde a persistência deveria acontecer; sem isso o comprovante/extrato do IB não tem fonte. Entra como investigação inicial da revisão de paridade PIX/Core.
1. **tx 199** (e2e `E46026562202606291343vkogk2uglvu`, R$ 1,00, 06-29): sem pacs.002/admi.002 até hoje; o camt.060 responde só sumário e o detalhe vem em arquivo (`RELACAO_LANCAMENTOS.csv`) cujo download NÃO está implementado na cabine (o legado usava parâmetros URLDOWNLOADARQUIVOSEXTRATOS/SUBDIRETORIOEXTRATO). Implementar o download mTLS do arquivo de extrato ou obter confirmação RTM/BACEN. ATENÇÃO: o hold dela já foi CONFIRMADO (débito consumado); futura rejeição exige crédito de volta manual documentado, não release.
2. **Consumo NATS do Core PixConsumer sem redelivery**: os eventos das txs 101-103 se perderam no restart do consumer (comportamento at-most-once). Avaliar consumo durável JetStream com ack explícito; hoje o backstop é o Oban PixStatusReconciliation (*/15min).
3. **10 `balance_blocks` órfãos ativos** de R$ 1,00 (06-25, pré-reseed de saldo): a soma não está refletida no blocked (0.00); liberar com release corromperia o contador. Expirar sem tocar no saldo, documentando.
4. **11 RJCT do lote de 06-27 com `completion_time` NULL** (o handler atual sempre preenche; backfill de dados históricos opcional).
5. **2 `accounting_events` PIX-SETTLE pending** das liquidações legítimas: `maybe_create_journal_from_event` falha silenciosamente (provável conta COSIF ausente). Diagnosticar o journal da cabine.
6. **Mensagens espelho SPB com `error_code` NULL** (o backfill foi em `spb_operations`; as `bacen_messages` outbound espelho das 7 ops + 3 GEN0001 antigas seguem NULL).
7. **Hop `find_tx_by_bacen_outbound_ref`**: verificar contra dados vivos no próximo admi.002 real (a lane de dados notou que nos casos antigos o BizMsgIdr estava no xml_content; envios novos persistem em `resource_id`, então a exposição residual é baixa; o StuckOutboundChecker alerta se algo prender).
8. **Instabilidade crônica do worker CPM** (kills "worker unresponsive" 10-22x/dia): investigar ordem persistência-antes-de-ack e considerar reintroduzir pacs.028 (removido em V2-01b) para reconciliação com evidência BACEN.
9. **LPI0006 2ª onda**: materializar o efeito financeiro (balanço grupo 43/espelho Conta PI); hoje a notificação é gravada e ACKada sem lançamento.
10. **`PixInOrphanReconciliation` é no-op** (`two_phase_pix_in_enabled=false` por design em HML); registrar na decisão de ativação do two-phase PIX-in.
11. **Sidecar `spb-mq-sidecar` por digest** (segue em tag mutável `homolog-latest`).
12. **Promoção do acervo legado a HML** (pacote da seção 7 do design; decisões de produto: exibição de `legacy_erro`, KPIs, tela de acervo SPB).

## 3. Gotchas operacionais novos desta sessão

- ECS execute-command: sessão exec herda o env do container no pix-api atual (`/proc/1/environ` dá Permission denied, mas não é mais necessário). NUNCA rodar `env` cru na sessão (risco de fragmentos de segredo no transcript). psql não existe nos containers; caminho confiável = `<release> rpc` gravando em arquivo + cat em sessão separada (a saída do exec pode truncar).
- `aws logs filter-log-events --max-items` pode devolver página vazia com NextToken (falso negativo); paginar ou omitir.
- Em `Repo.query` com `$N::jsonb`, passar o MAP Elixir direto (string Jason.encode! vira escalar jsonb e `||` concatena como array, destruindo o objeto).
- Não rotular `messages.status_id` via ref table em dados anteriores ao re-seed de 2026-07-01.

## 4. Como reproduzir/verificar

- ETL: `python3 etl/legacy_pix_spb/load_legacy_pix_spb.py verify` (97 itens) nos containers locais.
- Mapa canônico vivo: `SELECT id, code FROM monetarie_spi_ref.status_codes ORDER BY id` no mon_pix (1=PDNG...10=RTRN).
- SPB: `SELECT state, count(*) FROM bacen_messages WHERE created_at > now()-interval '7 days' GROUP BY 1` (nenhum processing antigo); LPI0006 do dia deve entrar como confirmed (~19:31Z).
- Core: teste de regressão `mix test test/monetarie/infra/nats/handlers/pix_handler_materialization_test.exs` (26 testes).
