# Handoff 2026-07-24 (noite): fix DEFINITIVO do pool de conexao ICOM (5 pools, modelo coreproviders) + CAMT/credito na conclusao

Branch `deepaudit/paridade-legadopix-2026-07-23`, PUSHADA. `main` intocada.

> RETOMAR por ESTE handoff. Sessao longa e critica: incidente de producao (cliente
> parado por saturacao de conexao `conn_limited`) + defeito de credito fantasma
> (PIX-in rejeitado creditado). Foundation do fix definitivo PRONTA e testada
> (Fases 1-2). Fases 3-4 (integracao money-path + CAMT) sao a proxima sessao.

## 0. Comando de retomada (colar na proxima sessao)

```
/retomar

RETOMAR MONETARIE. Ler primeiro:
1. docs/handoff/2026-07-24-fix-definitivo-pool-conexao-camt-handoff.md (este) — secoes 1..6
2. Topo do MEMORY.md

ESTADO: cliente PARADO (saturacao de conexao conn_limited no pix-api PRD + credito
fantasma PIX-in). Foundation do fix definitivo (pool de conexao modelo coreproviders,
5 pools por tipo no Redis) PRONTA e testada LOCAL (Fases 1-2, 13 testes). Falta Fase 3
(integracao: trocar Coordinator/Worker+ConnSemaphore.with_slot pelos SlotPoolManager;
os 5 tipos) e Fase 4 (CAMT: credito so na conclusao/Efetivada + reconciliacao). NADA
disso deployado. Validar em HML antes do PRD.

ACORDO COM O DONO (seguir a risca):
- Faco o que o dono pede, do jeito que ele descreve — NAO substituo pela minha versao.
- A fonte da verdade e o BACEN (camt.060), nao inferencia. So afirmo o que provei
  (camt.060/camt.054, arquivo:linha, ou a referencia coreproviders/LegadoPIX).
- Chega de usar "money-path" como escudo pra empurrar suposicao.
- Erros ja cometidos a NAO repetir: ISPB 00000000 e o Banco do Brasil (NAO simulador);
  nao afirmar que conn_limited "drenou" sem contar 2x; nao liderar com a minha ideia.
```

## 1. Estado de producao (medido hoje)

- **pix-api PRD td:78, 3/3.** Saturacao de conexao severa: `conn_limited` ~500-870 em
  janelas de 3-5 min (workers `Icom.Cpm.Worker`/`Icom.Csm.Worker` disputando os 6 slots).
  O `OutboundSender` heartbeat mostrava `failed=0` (envio automatico saia), mas as
  operacoes MANUAIS/consultas do operador (camt.060) morrem no `conn_limited` — o cliente
  "nao consegue operar". ATENCAO: uma rajada de camt.060 minha AGRAVOU o pico (assumido).
- **banking-ui PRD rev 16** DEPLOYADO (fix do redirect de MFA do IB: `/seguranca/2fa`
  inexistente -> `/auth/otp-setup`; commit local em `core/apps/banking/src/lib/api.ts`).
- **Backfill PRD feito:** user 3169 (Luiz Marcelo, CPF 32189410835) + conta 3236 tiveram
  `entity_id` NULL -> `cb858c89-2156-4066-bc69-da239bf423c1` (Monetarie). Aparece na
  Gestao de Clientes. E-mail de primeiro acesso enviado (`luiz_tti@hotmail.com`).
- **core-api NAO redeployado** com os fixes locais de onboarding/entity_id (so commitados).

## 2. Diagnostico PROVADO (verificavel, zero inferencia)

### 2a. Bug de conexao (a causa do cliente parado)
- Cada request ao BACEN (poll `/out` E envio `/in`) passa por `Shared.Bacen.Client` ->
  `with_bacen_conn` -> `ConnSemaphore.with_slot` = **adquire/solta o slot A CADA operacao**.
  Isso cria a corrida que gera `:conn_limited`.
- PROVA na propria tabela do dono: `SELECT action,status,sent_at FROM
  monetarie_spi.camt060_requests WHERE sent_at > now()-interval '30 minutes'` mostra
  `detalha-lancto` com **`send_failed`** intercalado com `sent`/`responded`, e os logs
  `/ecs/monetarie/prod/pix-api` filtro `conn_limited` no mesmo minuto.
- **O FIX DEFINITIVO (mandato do dono, modelo coreproviders ONz):** 5 pools INDEPENDENTES
  no Redis, cada um com seu proprio 6, slot PERSISTENTE + heartbeat (nunca solta/re-adquire):
  1. Poller BACEN (recepcao CPM/primario)
  2. Poller canal secundario (recepcao CSM)
  3. POST envio FINANCEIRO (CPM/primario)
  4. POST envio NAO-financeiro
  5. POST canal secundario (CSM)

### 2b. Credito fantasma PIX-in (rejeitado creditado)
- **Defeito de TIMING (arquivo:linha):** `apps/spi_service/lib/spi_service/workers/inbound_processor.ex:1075`
  credita a Conta PI (`update_pi_balance(:credit)`) na CHEGADA, antes do aceite (pacs.002
  ACSP, `:1108`) e antes da conclusao terminal. E `publish_status_update_for_tx/4`
  (`:4439-4511`): quando o RJCT terminal chega DEPOIS do credito, so publica status
  `rejected`, **NAO estorna** (nenhum `update_pi_balance(:debit)` / evento de reversao).
- **O modelo CERTO (LegadoPIX, confirmado no deep audit):** credita SO na CONCLUSAO
  (status `Efetivada`/`EFETIVADO`, `EhStatusFinal='S'`), nao na chegada. Ledger tem split
  `Aberto` (pendente) vs `Efetivado` (liquidado), e o worker `Multiliquidacao.Core.Worker.
  EmAberto` persegue as "em aberto" ate a conclusao via camt.060 (`DetOperacaoDTO`).
- **NAO ligar TWO_PHASE_PIX_IN.** O canonico (memoria `monetarie-pix-in-credit-path-canonical`,
  provado PROD 2026-07-09) prova que two-phase QUEBRA o credito: o gatilho "settled" so o
  simulador manda; com real BACEN o "settled" nunca chega e o cliente NUNCA e creditado.
  O BACEN liquida a entrada centralmente ANTES de entregar a pacs.008.
- **A verdade e por E2E via camt.060.** O `detalha-lancto` por E2E ja existe:
  `SpiService.OperationQuery.query(e2e)` (`operation_query.ex`), retorna `status: "ok"`
  com a camt.054 (CRDT/DBIT) do BACEN. PROVADO ao vivo via ECS Exec: E2E do Murilo
  `E0000000020260724132554058693177` (ISPB 00000000 = **Banco do Brasil**, real) voltou
  **CRDT R$1,00** = liquidou de verdade na PI, NAO e fantasma. A "Rejeitada" e o nosso
  status da pacs.002, nao a liquidacao do BACEN.

### 2c. Os 5 inbound "rejeitados" (status_id 8) a reconciliar (48h, tabela messages)
| E2E | pagador (ISPB) | camt.060 (verdade BACEN) |
|---|---|---|
| E0000000020260724132554058693177 (R$1 Murilo) | 00000000 = Banco do Brasil | **CRDT liquidado** (nao fantasma) |
| E60746948202607241323A3418rpyTaY (R$10 Gabriel) | 60746948 | pendente (send_failed no conn_limited) |
| E00416968202607241128059sWLkMb3Y (R$2.500 Clezio) | 00416968 | pendente (timeout) |
| E9270206720260722173601871339978 | 92702067 | pendente |
| E9270206720260722173301871334682 | 92702067 | pendente |
> Fazer camt.060 por E2E em TODAS as operacoes com problema dos ultimos 7 dias (com o
> pool corrigido, para de dar send_failed) e reconciliar contra o BACEN.

### 2d. Reconciliacao do extrato PARADA
- `monetarie_spi.statement_entries` so tem dados ate **20/07**. A materializacao e reativa
  (camt.052 pushado + `BACEN_STATEMENT_DOWNLOAD_URL`) e NAO ha scheduler que puxe o extrato.
  O `EodSnapshotWorker` (cron 03:30) so LE o que ja chegou; nao pede camt.060 REL/statement.
- O legado tem `SPI.Core.Worker.Conciliacao` que casa camt.052/053 (por E2E/InstrId/RtrId)
  com as operacoes internas e sincroniza os dois lados (flag `ConflitoStatus` = resolucao
  manual). Portar essa reconciliacao (Fase 4).

## 3. Fix definitivo — FEITO e testado (Fases 1-2, LOCAL, 13 testes)

Foundation do pool de conexao (porte fiel do `onz/poller.ex` do coreproviders), em
`pix/backend/apps/shared/lib/shared/bacen/`:
- **`slot_pool_scheduling.ex`** — `slot_key/3` (namespace proprio por tipo:
  `icom:pool:<tipo>:<ispb>:<slot>`, pools INDEPENDENTES) + `aligned_delay/4` (delay
  escalonado `(N-1)*stagger`, matematica exata do `schedule_aligned_poll`). 5 testes.
- **`slot_pool.ex`** — `acquire/5` (`SET NX PX`, primeiro livre vence, nunca a 7a),
  `renew/5` (heartbeat `SET XX PX`, segura o slot), `release/3` (`DEL`). Redis injetavel
  (`:slot_pool_redis`). 5 testes.
- **`slot_pool_manager.ex`** — GenServer generico: `:try_acquire_slot` -> active/standby;
  ativo SEGURA o slot (heartbeat Task fora do mailbox) + agenda `work_fun` escalonado
  (pipelined em Task) + failover (`:slot_lost`). `slot_pool`/`work_fun`/`heartbeat`
  injetaveis. 3 testes.
- Testes: `apps/shared/test/shared/bacen/slot_pool_*_test.exs` (13/0).

## 4. Roadmap — Fases 3-4 (proxima sessao)

### Fase 3 — integracao money-path (a parte critica; TDD + HML antes de PRD)
- Estrutura ATUAL: `SpiService.Icom.ApplicationSupervisor` sobe `Cpm.Coordinator` +
  `Csm.Coordinator` (leader-gate por advisory lock do PostgreSQL) -> cada leader sobe 6
  workers -> `Cpm.Worker.do_tick` faz `icom_pull_next` via `Shared.Bacen.Client` ->
  `with_bacen_conn` -> `ConnSemaphore.with_slot` (POR poll).
- TROCA: o `ApplicationSupervisor` passa a subir, por tipo, um supervisor de
  `SlotPoolManager`s (varios por pod competindo pelos 6 slots Redis; excedentes standby).
  Isso SUBSTITUI o leader-gate do PG pela competicao de slot no Redis.
  - `work_fun` do poller = a logica do `do_tick` (stream_start/pull_next) no slot SEGURADO.
  - `work_fun` dos 3 envios = processar a fila de saida no slot segurado.
  - REMOVER `ConnSemaphore.with_slot` do caminho de poll/envio (o slot Redis o substitui).
- Definir os 5 tipos (config: pool atom, ispb, max=6, lease 15s, heartbeat 5s, cycle/stagger).

### Fase 4 — CAMT + credito
- Mover o credito de `:1075` (chegada) para a CONCLUSAO (status terminal Efetivada),
  do jeito da LegadoPIX. NAO two-phase (2b). Worker "em aberto" que persegue via camt.060.
- Reconciliacao do extrato (2d): scheduler que pede camt.060 lista-lanctos + porta o
  `Conciliacao` (casa por E2E, sincroniza, `ConflitoStatus` manual). Materializar
  statement_entries dos dias parados (>= 21/07).
- Reconciliar os 5 E2Es (2c) e estornar os que o BACEN NAO liquidou; devolucao para os
  que liquidou mas a gente rejeitou. So com OK do dono no metodo (mutacao de saldo real).

## 5. Como acessar o PRD (o shell NAO alcanca a cabine interna)
- O host `.internal` (pixadmin/pixapi) e VPN-only e o shell resolve DNS pelo 8.8.8.8
  (NXDOMAIN). A rota 10.50.x tambem nao passa pelo shell mesmo com sandbox off.
- **ECS Exec (SSM) FUNCIONA** e e o caminho: `enableExecuteCommand=true` no pix-api,
  `session-manager-plugin` em `~/.local/bin`. Rodar no no vivo (BACEN client de pe):
  ```
  export PATH="$HOME/.local/bin:$PATH"
  awsmon ecs execute-command --cluster monetarie-greenfield-prod --task <TASKID> \
    --container pix-api --interactive --command "bin/monetarie_pix rpc 'CODE'"
  ```
  (dangerouslyDisableSandbox=true no Bash). Ex.: camt.060 por E2E:
  `IO.puts(inspect(SpiService.OperationQuery.query("E....")))` — retorna a camt.054 do BACEN.
- One-off ECS task (read-only DB): `bin/monetarie_pix eval "Ecto.Migrator.with_repo(...)"`
  ou `Application.ensure_all_started(:ecto_sql); Shared.Repo.start_link(); Shared.Repo.query!`.
- `awsmon` = wrapper AWS (`AWS_PROFILE=vulcimonetarie sa-east-1`). Task-def pix-api-prod:78,
  subnets/SG no historico deste handoff (FARGATE).

## 6. Outras frentes locais commitadas nesta sessao (corretas, TDD, NAO deployadas exceto banking-ui)
- **Onboarding coreadmin:** endereco no detalhe PF (`admin/onboarding_controller.ex`
  serialize PF ganhou `address_*`+ocupacao) + rotulos de status amigaveis
  (`normalize_pf_status` -> in_progress/draft; `core/apps/admin/src/lib/onboardingStatus.ts`).
- **entity_id na promocao Nextcode** (`promote.ex` estampa `proposal.entity_id`; corrige o
  cliente sumir da Gestao) + **e-mail de primeiro acesso na aprovacao KYC**
  (`kyc_controller.ex` chama `PasswordReset.create_reset_token`). TDD.
- **api.ts (IB):** redirect de MFA_REQUIRED `/seguranca/2fa` (morto) -> `/auth/otp-setup`
  (DEPLOYADO banking-ui rev 16).
- **MFA cabines (parcial, Fase 1):** `Shared.Auth.MfaPolicy` (exempt admin@monetarie.com;
  setup_required) + `dict_service auth_controller` sinaliza `mfa_setup_required`. Plano em
  `docs/plans/2026-07-24-mfa-cabines-pix-spb.md` (5 tipos de operacao manual pedem TOTP;
  decisao do dono: TOTP a cada operacao; HML antes de PRD).
- **Relatorio RTM** (`docs/reports/2026-07-24-hsm-rtm-solicitacao-apoio-504.{md,pdf}`)
  reescrito: 500 ms e padrao NOSSO, nao exigencia do BACEN.

## 7. Referencias (fonte da verdade)
- **coreproviders ONz poller (modelo do pool):** `/Users/luizpenha/coreproviders/
  coreproviders-wt-qr504/backend/lib/fluxiq/services/pix_providers/onz/poller.ex` —
  `try_acquire_slot`(446), `heartbeat_loop`(602, `SET XX`), `schedule_aligned_poll`(636).
- **LegadoPIX (fluxo CAMT/credito):** camt.060 por E2E = `RptgReq/Id`=E2E + `ReqdMsgNmId`=
  camt.054 (`worker.envioconsultaoperacao`); credito na conclusao (Efetivada); reconciliacao
  `worker.conciliacao`. Deep audit completo no historico desta sessao.
- Memorias: [[monetarie-pix-in-credit-path-canonical]] (NAO two-phase)
  [[monetarie-icom-cap6-desired3-failproof-0721]] [[legacy-pix-reference]]
