# Auditoria do money path PIX: AVIV (coreproviders) vs Monetarie, com foco em E2E, guards e indices

Data: 2026-07-16. Mandato do dono apos os P0 de producao de 15-16/07: investigacao seria, robusta e com total embasamento do money path, do guard, do E2E, dos indices e de tudo que trata o fluxo PIX, para termos resguardo e confianca no nosso ambiente.

Metodo: 6 trilhas paralelas de leitura read-only (5 sobre `/Users/luizpenha/coreproviders`, 1 sobre o nosso monorepo na main local `c381d1de`), cada afirmacao com evidencia arquivo:linha lida na fonte. O orquestrador re-verificou pessoalmente, por leitura direta dos arquivos citados, todas as afirmacoes estruturais e todas as lacunas residuais reportadas (secoes 4 e 5). Nada abaixo vem de inferencia; o que nao foi confirmado esta marcado NAO VERIFICADO. Nenhuma chamada AWS, nenhum banco vivo, nenhuma alteracao de codigo.

Convencao: `cp/` = `/Users/luizpenha/coreproviders/backend`, `mon/` = `/Users/luizpenha/monetarie`.

Contexto estrutural: a AVIV e participante INDIRETO (usa o provedor CloudPix OnZ; a OnZ assina e fala com o BACEN; recorde 1,53M tx/dia). Nos somos participante DIRETO com cabine propria. As protecoes da AVIV que interessam sao as independentes do provedor: ciclo de vida do E2E, maquina de estados com guards, unicidade no banco e malha de auto-cura.

---

## 1. Os tres incidentes nossos que motivaram o mandato (baseline)

1. **E2E duplicado por reuso de cache DICT (16/07, P0 reincidente)**: `E46026562202607161220btbqde54syq` transmitido 2x; BACEN rejeitou a 2a. Fix `fe39e538` (consulta cunha, pagamento QUEIMA com GETDEL, fail-closed 422) + barreira estrutural `c0f49dae` (tabela `monetarie_spi.e2e_outbound_burns`, PK em `end_to_end_id`, burn dentro da MESMA transacao que cria a linha OUTBOUND).
2. **Saldo congelado 19h (conta 2991, R$ 7.274,40)**: rejeicao do BACEN nao encerrava a `outbound_request` do envio manual; linha ficou em stage 2, que NENHUM sweeper cura (o `OutboundPendingRecoverySweeper` varre so stage 0; o `StuckOutboundChecker` so alerta); `BalanceGuard = min(TB, PG)` zerou o saldo. Fix `bd78762d` (rejeicao terminaliza a outbound_request).
3. **Estado terminal sobrescrevivel**: `handle_rejection` era o unico handler principal do StatusUpdater sem `guard_not_terminal`. Fix `810f1439`.

A causa de fundo comum: unicidade de E2E que so existia no changeset (indice nunca criado; `monetarie_spi.messages` e particionada e o unico indice unico e `(end_to_end_id, operation_time)`, que nao barra E2E repetido) e estados nao-terminais sem dono.

---

## 2. Como a AVIV blinda o mesmo money path (verificado no codigo atual)

### 2.1 Ciclo de vida do E2E no PIX-out

- **Gerador local roda POR SUBMISSAO, dentro da validacao** (`cp/lib/fluxiq/use_cases/payments/outbound_payment/pix.ex:938-950`), nunca lido de estado de tela ou cache. MANU sempre nasce com E2E fresco.
- **Cache HIT da consulta DICT NUNCA reenvia o E2E cacheado**: no hit, o pacs.008 sai com E2E provisorio FRESCO e a iniciacao vira MANU (para nao gerar "liquidacao DICT orfa"); no miss, sai com o E2E canonico devolvido pela OnZ (`cp/.../onz/adapter.ex:658-668`, verificado pelo orquestrador). Sem E2E utilizavel, aborta fail-closed (`adapter.ex:670-677`). Este e o antidoto exato do nosso incidente 1: nao existe caminho em que o E2E de consulta anterior seja retransmitido.
- **Reserva de E2E persistida e de uso unico**: `dict_e2e_reservations` com UNIQUE em `end_to_end_id`, `expires_at` (300s) e `used_at` (`cp/priv/repo/migrations/20260301000009:161-174`, verificado); `resolve_e2e_id` rejeita `reservation_already_used`/`reservation_expired` e marca `used_at` no ato (`pix.ex:909-933`, verificado). Sobrevive a restart de Redis e deixa trilha de auditoria do vinculo consulta-pagamento.
- **Unicidade em voo garantida pelo banco**: `outbound_requests` e NAO particionada de proposito, com indice UNICO parcial em `end_to_end_id` e em `idempotency_key` (`cp/priv/repo/migrations/20260301000003:230-238`, verificado). No maximo UMA operacao em voo por E2E; concorrencia morre no indice.
- Fragilidades da propria AVIV nessa frente (nao portar): aceita E2E explicito do chamador sem checar historico (`pix.ex:935-936`); a queima da reserva nao e atomica (get + update, sem `WHERE used_at IS NULL`); apos a liquidacao (linha deletada de `outbound_requests`), a unicidade historica de E2E nao existe (em `transactions` o unico e `(end_to_end_id, started_at)` por exigencia do particionamento). Nossa `e2e_outbound_burns` e mais forte nesse ponto.

### 2.2 Guards do PIX-out (maquina de estados)

- **Stage map documentado no schema**: 0 created, 1 linked_created (TB pending commitado), 2 api_sent, 3 settled, 4 fila de retry DICT, 5 quarentena, 99 rejected/voided (`cp/lib/fluxiq/schemas/payments/outbound_request.ex:69-76`, verificado).
- **Terminal = a linha E DELETADA** (delete-como-mutex): `move_to_transactions`/`move_to_failed` fazem `Repo.delete` com `stale_error_field` e `rollback(:already_resolved)` se outro caminho venceu (`cp/.../requests.ex:856-882`, verificado). Rejeicao tardia sobre settled nao acha a linha: vira `:not_found` logado. Impossivel sobrescrever terminal por construcao. Segundo arbitro: o proprio TigerBeetle two-phase (`pending_transfer_already_posted/voided` classificados idempotentes).
- **Erro AMBIGUO de infra NUNCA deleta nem faz rollback**: marcador `pending_recovery`, linha commitada em stage 0 para o sweeper idempotente, resposta 202 (`cp/.../orchestrator.ex:648-664`, verificado; incidente real conta 10203, R$ 4.850,27, 01/07 documentado no codigo).
- **StaleChecker a cada 2min RESOLVE (nao so alerta)**: stage 0 >60s (consulta TB por `user_data_128`; zero transfers = falha terminal; com transfers = promove), stage 1 >60s (reenvio idempotente), stage 2 >15s (consulta status no provedor e aplica desfecho sintetico), orfao >30min (force void GUARDADO), stage 2 >30min (QUARENTENA stage 5, sem tocar o hold) (`cp/.../atomic_payment_stale_checker.ex:17-39`, verificado).
- **VoidGuard**: antes de QUALQUER void, consulta a verdade do provedor e RECUSA o estorno se o BACEN liquidou (`cp/.../pending_transfers.ex:236-268`; nasceu do incidente R TORRES/King de R$ 105.093 em 20/04). Ressalva deles: fail-open se a consulta crashar.
- **Quarentena (stage 5) como estado explicito**: caso indecidivel nao fica orfao nem e resolvido as cegas; hold preservado, alerta imediato, escalacao por e-mail 6h/24h/48h, desfecho humano com MFA + evidencia tripla obrigatoria (mgmt + cabine + liquidacao) e rollback se o TB divergir da decisao.
- **Funil unico de rejeicao**: toda origem (RJCT do provedor, 4xx sincrono, TTL da fila, orfao) converge para void guardado + move_to_failed + webhook. Nao existe desfecho de rejeicao que deixe debito fantasma unilateral: ou o funil fecha, ou NADA fecha (hold TB e linha PG ambos vivos e visiveis, nunca so um deles).

### 2.3 Guards do PIX-in

- **4 camadas de dedup**: cursor de ACK condicionado a persistencia; inbox duravel `onz_lp_inbox` NAO particionada com UNIQUE GLOBAL em `message_id` (motivo documentado na migration: Postgres nao impoe UNIQUE global em particionada); `processed_messages` com `INSERT ... ON CONFLICT DO NOTHING` PG-primeiro (fail-closed em erro de DB); chave de dedup ciente do ciclo de vida (pacs.002 inclui TxSts; pacs.004 usa RtrId para nao descartar 2a devolucao parcial).
- **Deposito so com prova de liquidacao** (ACCC ou consulta MGMT `CONCLUIDA` fail-closed), intent consumido com GETDEL Lua (uso unico), e **IDs TB deterministicos derivados do E2E**: replay produz os MESMOS ids, TB devolve `:exists`, classificador trata como sucesso idempotente. Ultima linha que segura duplo credito mesmo se todas as outras falharem.
- **WAL por E2E** (`pending_pix_in_deposits`, PK = e2e_id) com maquina de estados e transicao forward-only guardada (`WHERE state = 'acsp_sent'`); estado terminal `rejected` que o recovery NUNCA revisita (nasceu do incidente de 10/07 deles: R$ 21.236,88 em 188 creditos sem lastro porque o RJCT nao encerrava o WAL e o sweep re-depositava; a docstring prometia um gate que nao existia no codigo. E a MESMA classe do nosso `bd78762d`, 6 dias antes do nosso).
- **Reconciliacao dirigida pela verdade externa**: `PixInOrphanReconciliation` a cada 15min compara PG vs TB e confirma orfaos contra o provedor antes de auto-depositar (cap 500/ciclo, advisory lock global de cluster); `PostDeployReconciliation` roda no BOOT de cada pod.
- **Devolucoes**: teto cumulativo persistido (`SUM` de pending+posted vs valor original) sob o MESMO advisory lock por conta; E2E de devolucao novo por operacao (D-prefix); dedup do fio por RtrId.

### 2.4 Banco de dados (a ultima linha de defesa)

- Idempotencia declarada em 3 camadas (`cp/lib/fluxiq/use_cases/pix/idempotency/idempotency.ex:5-8`): dedup do publisher, **INSERT atomico no PG como barreira primaria**, ids TB deterministicos.
- Unicidade GLOBAL vive em tabelas satelite NAO particionadas: `onz_lp_inbox (message_id)`, `processed_messages (message_id, source)`, `pix_idempotency_keys` (PK), `pending_pix_in_deposits` (PK e2e_id), `dict_e2e_reservations (end_to_end_id)`, `outbound_requests (end_to_end_id, idempotency_key)` parciais. Exatamente o padrao que adotamos agora com `e2e_outbound_burns`.
- Money path 100% `ON CONFLICT DO NOTHING` + sinal `{n, _}` (nunca `{:replace, ...}`); MERGE idempotente com swallow de `unique_violation` no espelho; PK composta byte-identica em retry porque `started_at` = commit timestamp do TB (DRIFT-01).
- `CHECK (amount > 0)` e id de 16 bytes em `transactions`; advisory lock `pg_advisory_xact_lock(band(account_id, ...))` serializa saldo+limite+insert+TB por conta, mesma chave usada pelo MED e tesouraria.
- Lacunas deles (nao copiar): `outbound_requests.stage` sem CHECK nem transicao guardada no UPDATE; `pix_return_requests` sem unicidade propria; sem CHECK de status/moeda em `transactions`.

### 2.5 Malha de auto-cura e a genealogia dos incidentes

Matriz estado x vigia praticamente completa (stage 0/1/2 pelo StaleChecker 2min; stage 4 pela fila Oban com TTL 2h; stage 5 por escalacao; WAL PIX-in pelo RecoveryWorker 5min com gate fail-closed; inbox pelo Recovery 1min; orfaos pela reconciliacao 15min + boot). Buracos residuais deles: stage 4 orfao de job (so tela admin), `WAL failed` e `inbox poisoned` sem monitor de acervo, monitores de drift TB/PG que param no Logger.

Cada mecanismo tem um incidente com valor em reais na certidao de nascimento (todos verificados em docs/codigo do repo deles): R$ 108k (7.745 creditos sem liquidacao) gerou o two-phase + WAL; R$ 121k (colisao de transfer id) gerou entropia + reconciliadores; R$ 105k (void de pagamento que o BACEN liquidou 90ms depois) gerou `api_sent_at` + QUARENTENA + VoidGuard; R$ 21k (RJCT nao encerrava WAL) gerou estado terminal `rejected` + gate MGMT real; "toda queda foi descoberta por cliente, nao por alarme" (15-16/05) gerou a bateria de watchdogs com OK-rate. A licao transversal: **nunca deletar/finalizar em erro ambiguo; nunca estornar sem consultar a verdade externa; caso indecidivel vira estado explicito com hold preservado e escalacao humana**.

---

## 3. O que os nossos fixes de 15-16/07 ja fecharam (paridade atingida)

| Dimensao | AVIV | Monetarie hoje (main `c381d1de`) |
|---|---|---|
| E2E manual fresco por submissao | gerador dentro da validacao | `f65a5116`: MANU sempre gera (payment_controller) |
| E2E de consulta e de uso unico | reserva persistida com used_at | `fe39e538`: GETDEL + eco so vale se casar com cache vivo (422 `E2E_MISMATCH`/`DICT_CONSULT_REQUIRED`) |
| Unicidade de E2E no banco | UNIQUE parcial em outbound_requests (em voo) | `c0f49dae`: `e2e_outbound_burns` PK e2e, burn NA MESMA transacao da linha OUTBOUND, nos 2 caminhos (HTTP `payment_controller.ex:378-381` e NATS `core_event_processor.ex:510-513`). MAIS FORTE que a AVIV: cobre tambem o historico, nao so o em-voo |
| Rejeicao encerra a reserva | funil unico void+move_to_failed | `bd78762d`: `terminalize_outbound_request` no ramo rejected (`pix_handler.ex:445`) |
| Guard de estado terminal | terminal = linha deletada (estrutural) | `810f1439`: `guard_not_terminal` no handle_rejection; Core ja tinha `materialize_status` anti-regressao e o proprio padrao delete-como-mutex no `move_to_transactions`/`move_to_failed` |
| Dedup de consumo | processed_messages + claim atomico | `DurableConsumer` + `MsgIdentity` + `processed_messages (message_id, source)` |
| Deposito idempotente PIX-in | ids TB deterministicos do E2E | transfer_id deterministico SHA-256 do reference no `Wallet.deposit` |

Onde ja somos ESTRUTURALMENTE melhores (nao regredir): participante direto (24-28ms vs p50 471ms), zero consulta DICT no ato do pagamento, cadeia fail-closed regulatoria (XMLDSig + sancoes + XSD oficial pos-assinatura), outbox atomico com enforcement triplo, pre-ACK durability no ICOM, noturno Res.142 enforcado.

---

## 4. LACUNAS RESIDUAIS CONFIRMADAS no nosso ambiente (verificadas pelo orquestrador, arquivo a arquivo)

Ordenadas por severidade. "Confirmada" = eu li o codigo citado nesta auditoria.

### R1 (P0) Envio manual "Construir Mensagem" fora de TODAS as barreiras do E2E
`mon/pix/backend/apps/settlement_service/lib/settlement_service_web/controllers/message_controller.ex:969-971`: `dispatch_message("pacs.008", xml, _params)` chama `Shared.Bacen.SpiClient.send_payment(xml)` direto. O `end_to_end_id` e campo do formulario (`message_form_catalog.ex:79`) e o builder aceita `p[:end_to_end_id] || generate` (`message_builder.ex:165`). Grep por `E2eBurn`/`register_debtor_debit`/`validate_debit` no controller = 0 ocorrencias. Ou seja: a mesma classe dos P0 de 15-16/07 (E2E reusado/duplicado, e pacs.008 sem lastro de debito no Core) continua aberta por esta porta administrativa. Mitigadores existentes (RBAC `can_create`, SendGate de alcada, Pacs008SendValidator) nao tocam o E2E. Recomendacao: rotear o pacs.008 do Construtor pelo MESMO funil do pagamento (burn + debito) ou, no minimo, `E2eBurn.burn` + E2E sempre gerado (nunca do formulario).

### R2 (P0/P1) Liberacao de hold no Core NAO e idempotente
`mon/core/backend/lib/monetarie/use_cases/transaction.ex:92-93`: `FundHold.release_funds` cria transfers TB novas com `ID.generate()` ALEATORIO (o ramo `:exists` da linha 121 e letra morta com id aleatorio). O ramo `"rejected"` do `handle_tracked_status` (`pix_handler.ex:429-433`) executa `release_held_funds(tx)` sem guarda de terminal (a anti-regressao do `materialize_status` protege so a escrita do status) e `hold_id` nao e limpo. Duas entregas de rejeicao com chaves de dedup DISTINTAS (eventos diferentes para a mesma transacao) podem creditar o cliente DUAS vezes. Alem disso coexistem dois mecanismos de devolucao de hold sem coordenacao: `release_funds` (transfers aleatorias) e o `Wallet.deposit` deterministico do StaleHoldChecker. Recomendacao (padrao AVIV): id deterministico derivado de `hold_id` (replay = `:exists` = no-op), limpar `hold_id` na transacao apos o release, e release condicionado a transicao real de status (so quando `materialize_status` efetivou a mudanca).

### R3 (P1) OutboundSender sem guarda de "ja enviada": reentrega re-POSTa a pacs.008
`mon/pix/backend/apps/spi_service/lib/spi_service/workers/outbound_sender.ex:79-116`: o dispatch vai direto a validacao/assinatura/POST sem checar se a mensagem ja foi enviada/terminal; `update_transaction_status` e `update_all` cru (`:540-560`) que regride status para 2. Uma reentrega at-least-once de `monetarie.spi.outbound.send` (NAK anterior, redelivery do JetStream, replay) re-assina e re-POSTa a MESMA pacs.008. O burn do E2E acontece na CRIACAO, nao no envio, entao nao protege este salto. A AVIV nunca reenvia em stage 2: consulta status e aplica desfecho. Recomendacao: guarda por status (so envia se status atual e pre-envio) + registro de envio (idempotencia por message_id no proprio sender).

### R4 (P1) Cabine: RTRN e CANC sem `guard_not_terminal`
`mon/pix/backend/apps/spi_service/lib/spi_service/workers/status_updater.ex:599-622` (`handle_return_confirmation`: seta RTRN + `credit_return_balance`, idempotencia do credito NAO VERIFICADA) e `:624-648` (`handle_cancellation`: seta CANC + `credit_back_and_release`; o dinheiro nao dobra porque `revert_block` e guardado por estado do block, mas o STATUS regride de terminal). O fix `810f1439` cobriu os 4 handlers principais; estes dois ficaram de fora. Mesma classe do incidente 3. Recomendacao: `guard_not_terminal` nos dois (com a semantica correta: RTRN legitimamente sucede ACSC no fluxo de devolucao, entao a guarda deve barrar apenas regressoes indevidas, ex.: RTRN sobre RJCT, CANC sobre ACSC) + prova de idempotencia do `credit_return_balance`.

### R5 (P1) Recorrencia (PIX Automatico) publica pacs.008 sem burn
`mon/pix/backend/apps/spi_service/lib/spi_service/recurrences/execution_worker.ex:263` gera E2E e publica `monetarie.spi.outbound.send` (`:308-313`) sem `E2eBurn`. Combinado com R3, uma reentrega desse evento retransmite a pacs.008. Recomendacao: burn na mesma transacao do enqueue (o modulo `Shared.E2eBurn` ja existe; e 1 chamada).

### R6 (P1) Stage 1/2 do Core: detecta mas nao cura (flags OFF)
Pos-`bd78762d` o encerramento na rejeicao e event-driven. Se o evento terminal se perder, a linha stage 1/2 volta a congelar o saldo do cliente ate operador agir: `OutboundPendingRecoverySweeper` (cron */2) varre so stage 0; `StuckOutboundChecker` (cron 10min) detecta 1/2 >30min mas so alerta; as tres flags de desfecho (`PIX_OUT_PENDING_RECOVERY_VOID_ENABLED`, `STUCK_OUTBOUND_AUTO_RESOLVE_ENABLED`, `PIX_OUT_RETRY_QUEUE_ENABLED`) nascem false (`core runtime.exs:66-70, 110, 118-119`). O proprio comentario do fix admite (`pix_handler.ex:441-444`). Recomendacao (padrao AVIV, respeitando a regra do dono de desfecho so com evidencia BACEN): resolvedor de stage 2 que CONSULTA a verdade (status na cabine por E2E, camt.060/acervo ICOM) e aplica o desfecho provado; para o indecidivel, quarentena explicita com alerta escalonado, nunca void cego. O `PixStatusReconciliation` (cron 15min) ja materializa `transactions` presas consultando a cabine; falta o equivalente para `outbound_requests`.

### R7 (P1, candidato a defeito funcional) Gerador de E2E do Core produz 34 caracteres
`mon/core/backend/lib/monetarie/use_cases/payments/outbound/pix.ex:412-418`: `E + ISPB(8) + YYYYMMDDHHMMSS(14) + 11 = 34 chars`. O XSD oficial (`pix/backend/apps/shared/priv/xsd/spi/v5.12.1/pacs.008.spi.1.15.xsd`, `EndToEndIdType`) exige maxLength 32 e pattern com timestamp de 12 digitos (verificado). Para pagamento POR CHAVE a cabine ignora o E2E do Core (inocuo). Para pagamento SEM chave, o `core_event_processor.ex:358-359` aceita o E2E do Core verbatim: se este fallback for exercitado, a pacs.008 e barrada no gate XSD fail-closed do OutboundSender e o pagamento falha sempre (dinheiro nao sai, mas o fluxo quebra). A primeira clausula (`:395-396`) tambem aceita E2E ecoado pelo cliente da API (superficie coberta a jusante pelo burn, mas desnecessaria). Recomendacao: alinhar ao formato canonico de 32 (o gotcha do repo ja manda usar gerador unico) e testar o caminho MANU-via-API ponta a ponta. NAO VERIFICADO se o fallback e alcancado em fluxo vivo.

### R8 (P2) `status_update` engolido quando existe outbound_request viva
`mon/core/backend/lib/monetarie/use_cases/payments/atomic_payment_handler.ex:65-71`: `maybe_handle(payload, "status_update")` cai no catch-all `:handled`, impedindo o mapeamento do PixHandler. Compensado pelos eventos dedicados settled/rejected; vira perda so se apenas o status_update chegar.

### R9 (P2) Dedup de consumo: fail-open + marca pos-handler
`mon/core/.../message_dedup.ex:46-49` (`already_processed?` devolve false em erro de query) e `durable_consumer.ex:143-158` (marca gravada depois do handler; crash entre os dois = reprocesso). Aceitavel SE a idempotencia de negocio for real; hoje R2 quebra essa premissa no ramo de rejeicao. Fechar R2 fecha o risco pratico de R9.

### R10 (P2) Higiene
(a) `e2e_outbound_burns` sem poda (decisao explicita da migration; criar cron de retencao e decisao separada, registrar). (b) `unique_constraint(:end_to_end_id, name: :spi_messages_e2e_id_unique)` no changeset da cabine (`shared/schemas/spi/transaction.ex:97`) referencia indice que NAO existe em nenhuma migration: remover ou substituir por referencia real, para nunca mais parecer defesa sem ser. (c) Divergencia de docstrings em modulos de dinheiro (a AVIV pagou R$ 21k por uma docstring que prometia gate inexistente; nos ja tivemos o contrato "Core catch-all credits immediately" morto em 25/06).

---

## 4.1 Status de execucao (2026-07-16 noite 2 — arco C)

R1-R5, R7-R10 fechados nas sessoes anteriores (16/07). Nesta sessao, com OK do dono, foram implementados **R6 + ports 5, 6, 7 e 8** (commits locais `53d47b9e` core + `069a72d7` pix, TDD):

- **R6 / port 5 (resolvedor stage 1/2 dirigido por evidencia):** `StuckOutboundChecker` passou a consultar a cabine por E2E (`CabinStatusLookup.by_end_to_end_id`) quando nao ha evidencia LOCAL. Cabine confirma REJEICAO terminal (dinheiro nao saiu) -> terminaliza via `VoidGuard.safe_void` (atras da flag `STUCK_OUTBOUND_AUTO_RESOLVE_ENABLED` existente, money-safe, nunca void cego). Settled polido / ausente / indecidivel -> **quarentena explicita** (nao muda stage nem toca dinheiro; o saldo segue protegido pela reserva no TB) + alerta humano. Cabine indisponivel -> comportamento anterior preservado. Novo knob `STUCK_OUTBOUND_QUARANTINE_MINUTES` (default 120).
- **Port 6 (alerta HUMANO):** `Monetarie.UseCases.Alerts.Operational.raise/1` — ponto unico que persiste em `alerts` (+ notification broadcast a `user_id: "all"`, ja exposto em `/api/v1/alerts` e `/api/v1/metrics/alerts-summary`), com dedup por `dedup_key` na janela de alerta aberto. Ligado no `StuckOutboundChecker` e no `DlqMonitorWorker`. **BalanceGuard ficou de fora do gate de proposito** (divergencia in-flight e normal; alerta de balanco pertence a uma varredura de reconciliacao, nao ao caminho quente ANS 1,6s).
- **Port 7 (teto cumulativo de devolucoes sob lock):** `Shared.Returns.CumulativeCap.guard/4` — teto por E2E original sob `pg_advisory_xact_lock`, DENTRO da transacao que cria a pacs.004. Corrige os 3 defeitos reais de over-refund achados no mapeamento: caminho partner/v2 sem teto; soma via JOIN a `payments` (lia 0); read-then-insert sem lock. Soma pela fonte real `json_input->>'amount'` (centavos), conta pendentes+liquidadas, ignora rejeitadas (8/9). Fail-closed; sem valor original confiavel nao barra. Ligado nos DOIS caminhos (`handle_return_request` e `ReturnProcessor`).
- **Port 8 (reserva persistida consulta-pagamento):** `Shared.Dict.E2eReservation` + migration `20260716230000_create_dict_e2e_reservations` — espelho DURAVEL do vinculo E2E+recebedor, escrito best-effort junto com o `E2eCache` e lido como FALLBACK quando o Redis esta vazio (restart do ElastiCache entre consulta e pagamento). Redis segue primario; tudo fail-soft.

Ports 1-4 permanecem como estao (fechados nas sessoes anteriores). NAO implementado dos itens B (aguardam decisao do dono): fluxo de agendamento TED ponta a ponta, outbox cross-repo, heuristicas de sender_ispb/local_instrument, event ausente, transient catch-all.

---

## 5. O que portar da AVIV (priorizado, mapeado as lacunas)

| # | Port | Fecha | Esforco |
|---|---|---|---|
| 1 | Funil unico do pacs.008: TODO caminho que transmite (Construtor, recorrencia, futuros) passa por burn + lastro de debito | R1, R5 | S |
| 2 | Release de hold idempotente por id deterministico + release condicionado a transicao efetiva + limpar hold_id | R2, R9 | S/M |
| 3 | Guarda de envio no OutboundSender (status pre-envio + idempotencia por message_id); em duvida, consultar em vez de reenviar | R3 | S/M |
| 4 | `guard_not_terminal` em RTRN/CANC com semantica de transicoes legitimas | R4 | S |
| 5 | Resolvedor de outbound_requests stage 1/2 dirigido por evidencia (consulta cabine/camt.060/acervo) + quarentena explicita com escalacao humana para o indecidivel (nunca void cego; VoidGuard antes de qualquer estorno automatico futuro) | R6 | M |
| 6 | Alerta HUMANO (nao so Logger) para: divergencia do BalanceGuard, outbound_request em stage 1/2 velha, DLQ/poison, burns rejeitados (`E2E_ALREADY_USED` e sinal de bug ou ataque) | transversal | S |
| 7 | Teto cumulativo de devolucoes num ponto unico sob lock (modelo `cumulative_returned`) se/quando houver devolucao parcial concorrente | preventivo | M |
| 8 | Reserva persistida consulta-pagamento (equivalente `dict_e2e_reservations`) como auditoria do vinculo e resiliencia a restart do Redis (o E2eCache hoje e so Redis) | preventivo | S |

NAO PORTAR (ja canonico): two-phase PIX-in (PROIBIDO no nosso modelo), long-poll/slots/cursor de provedor, remocao de broker, deadlines deles sem recalibrar ao ANS 1,6s.

---

## 6. NAO VERIFICADO / limites desta auditoria

1. Estado dos indices e flags em banco/task-def VIVOS (HML/PROD) dos dois lados: tudo aqui e codigo + migrations. Em especial: confirmar que a migration `20260716160000` (e2e_outbound_burns) sera aplicada em HML e PROD no deploy.
2. Se `SpiClient.send_payment`/`send_signed_message` persiste linha em `bacen_outbound`/`messages` no caminho do Construtor (R1): nao lido; nao muda a ausencia de burn/debito.
3. Idempotencia de `Balances.update_balance` no credito de devolucao da cabine (R4).
4. Se o fallback de 34 chars (R7) e alcancado em fluxo vivo; exige teste dirigido.
5. Comportamento do BACEN ao receber pacs.008 IDENTICA repetida (mesmo E2E, mesmo conteudo, caso R3): o incidente de 16/07 provou rejeicao para conteudo DIFERENTE; para identico nao ha evidencia no acervo.
6. Na AVIV: valores vivos de env (`ONZ_LP_INBOX_CONSUMER` etc.), desfechos financeiros de 2 incidentes antigos, conteudo integral dos xlsx. Listados nos relatorios das trilhas.

## 7. Rastreabilidade

Trabalho em voo relacionado (outras sessoes, hoje): barreira `e2e_outbound_burns` commitada em `c0f49dae`; fix multi-member do coreadmin em `c381d1de`; ambos pushados nesta sessao (`origin/main = c381d1de`). Os 3 fixes dos incidentes (`f65a5116`, `fe39e538`, `bd78762d`, `810f1439`) ja estavam na main.

Relatorios-fonte das trilhas: mapeamento AVIV de 09/07 em `docs/reports/2026-07-09-mapeamento-aviv/` (conferido contra o codigo atual; divergencias anotadas, ex.: root_id do TB hoje e bit-packing reversivel, nao SHA-256). As evidencias arquivo:linha desta auditoria foram lidas em 16/07 no working tree atual dos dois repositorios.
