# Handoff 26/07 noite: rodada 3 do DICT contra o LegadoPIX, e as 3 chaves no BACEN

Estado: `main` == `origin/main` == `3bde8bc1`. pix-api **PRD td95 / HML 226**,
tag `*-3bde8bc1-dict-20260726`, guard de digest MATCH, TG 3/3 healthy nos dois.
Suites: shared 2017, dict_service 490, spi_service 1158, settlement 1039, todas
com 0 falhas.

## O que motivou a rodada

A pergunta do dono: "por qual motivo voce nao confronta com o LegadoPIX/*?".
A rodada 2 tinha fechado tres itens como "nao verificado". Decompilando o legado
(`ilspycmd`), os tres viraram defeito provado.

## As 3 chaves, resolvidas no BACEN

| chave EVP | conta antes | conta agora | tipo |
|---|---|---|---|
| `31eaebe4-...` Gabriel | `2031` | `0000009824` | TRAN |
| `7d98c4c8-...` Gabriel | `2031` | `0000009824` | TRAN |
| `82d58d65-...` Luiz | `3236` | `1000012` | TRAN |

Prova, e nao afirmacao: o `GET /cids/events` do BACEN registrou exatamente 3
`REMOVED` (os CIDs velhos) e 3 `ADDED` as 21:17:57, e o `GetEntryByCidResponse`
assinado pelos CIDs novos devolve conta certa e `TRAN`. O CID local tambem foi
atualizado; sem isso a reconciliacao de CID acusaria divergencia nas tres.

O numero da conta **nao foi chutado**. A regra esta no nosso proprio codigo
(`open_account.ex`, `resolve_account_number/1`): conta migrada guarda 9 digitos
com o `check_digit` separado; conta nova guarda `"base-dv"`, ja com o digito e
com hifen. Aplicada aos dados reais, a regra bate em **278 das 306 chaves**. As
28 restantes sao essas 3 (que levavam o id interno) e 25 com um zero a esquerda
a menos, que ficam como frente menor. As outras 303 seguem CACC no BACEN, por
decisao do dono.

A conta 3236 e a unica das 151 ativas com separador no numero (`100001-2`), que
e exatamente o caso que o `strip_account_separators/1` cobre no limite do fio.

## Os 4 defeitos que o legado entregou

1. **`bec43c7c` — consultar marcador de fraude devolvia o id ERRADO.**
   Nao ha fixture `GetFraudMarkerResponse.xml` no repo; a forma veio do
   `ExtendedFraudMarker` do simulador do legado, que traz `<InfractionReport>`
   ANINHADO dentro de `<FraudMarker>`. Nosso guard SAX era so
   `"FraudMarker" in stack`, condicao que continua verdadeira dentro do bloco
   aninhado: o `<Id>` da infracao sobrescrevia o do marcador, e o
   `<ReporterParticipant>` (quem reportou) era descartado.

2. **`19b65b71` — toda ESCRITA no DICT saia sem orcamento de balde.**
   `POST /entries/` e `PUT /entries/{Key}` devolviam `nil` no `bucket_for/2`.
   O acerto em lote de chaves so descobriria o limite tomando 429, que e
   degradacao do participante.

3. **`5e2bdd6f` + `4be6ef84` — o prazo de reivindicacao era NOSSO.**
   Criavamos a claim com 14/30 dias calculados aqui e **bloqueavamos operacao**
   por essa data, enquanto o BACEN mandava `ResolutionPeriodEnd` e
   `CompletionPeriodEnd` na resposta e o parser ja os extraia. Ninguem usava.
   O legado nunca calculou prazo: `MontaReivindicacaoRetorno` le da resposta,
   sempre. Na fixture oficial os dois prazos sao D+7 da criacao: nem 14, nem 30.
   O mesmo descarte existia nas 4 transicoes do ciclo de vida, cujas respostas
   trazem os prazos justamente porque o BACEN pode move-los.

4. **`3bde8bc1` — montar entrada com a data da propria chave estourava.**
   `format_iso8601/1` nao tinha clausula para `Date`, e o schema declara
   `opening_date, :date`: `FunctionClauseError` antes de assinar. Achado
   rodando o acerto em PRODUCAO. A data vai como `03:00Z`, meia-noite de
   Brasilia; `00:00Z` seria 21:00 do dia anterior e mudaria o dia de abertura.

## O BACEN publica a capacidade dos baldes, e nunca perguntamos

`GET /policies/` devolve 29 politicas com `AvailableTokens`, `Capacity`,
`RefillTokens` e `RefillPeriodSec`. O legado ja consumia esse endpoint
(`ReceiveServicePolicies`). Agora existe `calibrate_all_from_bacen/1`: uma
chamada calibra todos os baldes conhecidos.

Valores REAIS do nosso ISPB em PRD, medidos apos o deploy:

| politica | capacidade | refil |
|---|---|---|
| `ENTRIES_WRITE` | 36000 | 200/10s |
| `ENTRIES_UPDATE` | 600 | 600/60s |
| `ENTRIES_READ_PARTICIPANT_ANTISCAN` | 250 | 25/60s (categoria **G**) |
| `KEYS_CHECK` | 70 | 70/60s |
| `CIDS_FILES_WRITE` | 200 | - |

Dois ajustes de premissa: a leitura assumia categoria H (50), mais restritiva
que a real; e o balde de CID files no nosso doc estava como "40/dia" e com o
nome errado (`CIDS_FILE_CREATE`; o BACEN chama `CIDS_FILES_WRITE`).

Balde de escrita nasce **sem** capacidade e so orca depois de calibrado. Nao
inventar numero para parecer orcado e regra, nao estilo.

## Retratacao

A tabela de baldes da rodada 2 trazia numeros ao lado de cada politica
(`ENTRIES_UPDATE 600/min` etc.). Aqueles numeros nao vieram do BACEN: batem
exatamente com o `LoadListPolicies()` do **simulador** do legado. Foram
removidos do documento.

## Aberto

1. **`GET /entries/{Key}` recusado** quando o `PI-PayerId` somos nos ou o
   proprio titular da chave: a conexao fecha e o erro fica invisivel (defeito
   §5.1, corpo do erro perdido). O caminho do dinheiro nao e afetado, porque
   usa pagador real; confirmado por `GetEntryResponse` bem-sucedido no dia e
   pelas 19 pacs.008 enviadas em 2 dias. Precisa de um `GetEntry` de
   reconciliacao que o BACEN aceite, ou usar sempre `GetEntryByCid`.
2. **`parse_cancel_funds_recovery_response` sem prova**: nao ha fixture e o
   legado nao implementa funds recovery (MED 2.0 e posterior a ele).
3. **Modelo de `Refund` do legado e mais novo que o nosso**:
   `EffectiveRefundedAmount`, `FundsRecoveryId`, `MonitorAccount` e
   `RefundAccount` completo. Evidencia de versao, nao prova de defeito nosso.
   Precisa do contrato 2.12.1 de devolucao; inventar tag em XML assinado seria
   repetir o erro do dia.
4. **25 chaves com um zero a esquerda a menos** no numero da conta no BACEN.
5. **HML: DICT 400 de 2 em 2 minutos**, continuo ha horas, anterior ao deploy.
6. `conn_limited` aparece so na janela de sobreposicao do rollout (65 eventos em
   1 minuto, zero antes e depois). Transiente esperado do teto de conexoes do
   ICOM, nao regressao.

Detalhe completo: `docs/reports/2026-07-26-gap-analysis-api-dict-bacen.md`,
secao 9.
