# Handoff — camt.060 má formação + revisão FULL das mensagens SPI (2026-06-24)

Sessão longa. Tudo abaixo é EMPÍRICO (rpc na pix-api viva via ECS exec). Regra do dono:
BACEN é a referência correta; quando ele rejeita, o defeito é NOSSO; nada de inferência
sem prova. Ver memórias [[owner-bacen-is-truth-find-our-defect]], [[monetarie-camt060-bacen-reject]].

## Fonte autoritativa (USAR SEMPRE)
`/Users/luizpenha/mwbank/md/v5.12.1/` — catálogo BACEN v5.12.1:
- `exemplos/<msg>/*.xml` — exemplos OFICIAIS de cada mensagem (ground truth de estrutura).
- `xsd/<msg>.spi.X.Y.xsd` — schemas (idênticos aos nossos em `apps/shared/priv/xsd/spi/v5.12.1/`).
- `Manual das Interfaces de Comunicação-1.12.md`, `Catalogo_..._Volume_VI_Versao_512.pdf`.
Comparar SEMPRE o XML que GERAMOS contra `exemplos/` + validar com `xmllint --schema`.

## camt.060 — defeitos PROVADOS no nosso código
Builder `Shared.Bacen.SpiClient.build_camt060_request/1` (spi_client.ex ~426-454) gera um
`RptgReq` no padrão ISO completo, divergente do exemplo BACEN `camt.060_SALDO_MOMENTO_msg.xml`.
Erros (cada um confere contra o exemplo + XSD `ReportingRequest5`):
1. emite `<Acct><Id><Othr><Id>` — NÃO existe no perfil SPI (remover).
2. `<AcctOwnr><FinInstnId>...` SEM o wrapper `<Agt>` (XSD: AcctOwnr=Party40Choice -> `<Agt>` -> FinInstnId -> ClrSysMmbId -> MmbId).
3. `<ReqdMsgNmId>` na ORDEM errada (deve vir ANTES de AcctOwnr).
4. FALTA `<ReqdBalTp><CdOrPrtry><Prtry>CSA</Prtry></CdOrPrtry></ReqdBalTp>` — é o código que diz ao BACEN QUAL consulta é. (O "fix" AVLB->camt.053 anterior REMOVEU o ReqdBalTp em vez de pôr o código certo.)

RptgReq correto p/ saldo no momento (= nosso "saldo-atual"):
```
<RptgReq>
  <ReqdMsgNmId>camt.053</ReqdMsgNmId>
  <AcctOwnr><Agt><FinInstnId><ClrSysMmbId><MmbId>{ISPB}</MmbId></ClrSysMmbId></FinInstnId></Agt></AcctOwnr>
  <ReqdBalTp><CdOrPrtry><Prtry>CSA</Prtry></CdOrPrtry></ReqdBalTp>
</RptgReq>
```
Códigos por tipo de consulta (extraídos dos 7 exemplos `exemplos/camt060/*.xml`), todos com
`AcctOwnr/Agt/FinInstnId/ClrSysMmbId/MmbId` + `<Sgntr></Sgntr>` placeholder no AppHdr:
- SALDO_MOMENTO (saldo-atual): ReqdMsgNmId=camt.053, ReqdBalTp/Prtry=**CSA**, sem FrToDt.
- SALDO_DATA_ANTERIOR (saldo-na-data): camt.053, Prtry=CSA, + `<RptgPrd><FrToDt><FrDt>AAAA-MM-DD</FrDt></FrToDt><Tp>ALLL</Tp></RptgPrd>` (ver exemplo p/ ordem exata de RptgPrd vs ReqdBalTp).
- SOLIC_REL_LANCAMENTO (lista-lanctos): ReqdMsgNmId=**camt.052**, Prtry=**REL**, + FrToDt/Tp=ALLL.
- SOLIC_ARQUIVO_TRD: camt.052, Prtry=**TRD**, + FrToDt/Tp=ALLL.
- SOLIC_REMUNERACAO_CONTA_PI: camt.053, Prtry=**CRE**, + FrToDt/Tp=ALLL.
- SOLIC_LANCAMENTO / _STR (detalha-lancto): ReqdMsgNmId=**camt.054** (sem ReqdBalTp; ver exemplo p/ campos de detalhe).
Implementar build_camt060_request por tipo, copiando a ORDEM/estrutura EXATA de cada exemplo.

## Bug 2 (PROVADO): verify_spi quebrado (KeyInfo digest)
`Shared.Crypto.XmlVerifier.verify_spi/1` retorna `{:error, {:digest_mismatch, :key_info}}` para
mensagens que o BACEN ACEITA (testado: nosso pibr.001 assinado, que o BACEN responde com pibr.002,
FALHA no nosso verify_spi com esse mesmo erro). Ou seja, o verificador recomputa o digest da
Reference KeyInfo (URI="#key-info-id") errado (provável: C14N/namespace do nó KeyInfo). Afeta a
verificação de assinatura INBOUND também. Corrigir (xml_verifier.ex, ref KeyInfo) e validar
re-verificando um pibr.001 nosso (deve dar OK) antes de confiar nele.

## O que está PROVADO empiricamente (head-to-head, mesmas condições, task viva)
- `echo()` (pibr.001) -> BACEN devolve **pibr.002** (latency 93ms). FUNCIONA pelo `/api/v1/in/{ispb}/msgs`.
- camt.060 (estrutura corrigida CSA + placeholder Sgntr + mesma assinatura do signer que o BACEN
  aceita no pibr.001), nos canais primary E secondary -> **admi.002** "Erro no processamento da ICOM /
  Schema desconhecido, não habilitado para uso ou canal incorreto".
- Logo ELIMINADOS como causa do reject da camt.060: estrutura do corpo (bate byte-a-byte com o
  exemplo BACEN), canal (primary e secondary), transporte/endpoint/assinatura genérica (pibr.001
  prova que funcionam). `icom-sec-h:17522` alcançável (CONNECT 40ms).
- camt.060 NUNCA retornou camt.05x (0 em bacen_inbound EVER). Saldo segue source manual_homolog R$1M.

## ABERTO (a resolver na próxima sessão) — NÃO concluir sem prova
Mesmo com a camt.060 corrigida (idêntica ao exemplo) + assinatura como a do pibr.001 aceito, o
BACEN rejeita. A ÚNICA "causa nossa" ainda não 100% descartada: a ASSINATURA da camt.060
especificamente pode estar inválida por um edge-case do signer (o verify_spi quebrado NÃO permite
confirmar). PRÓXIMO PASSO decisivo: validar a assinatura da camt.060 com ferramenta INDEPENDENTE
(`xmlsec1 --verify` com a cadeia ICP-Brasil) OU consertar verify_spi e re-verificar:
- se a assinatura da camt.060 for INVÁLIDA -> bug do signer p/ camt.060 = a causa (nosso código).
- se for VÁLIDA -> a estrutura+assinatura estão certas e o fator é específico do tipo camt.060
  (checar Catálogo Vol VI v5.12 p/ restrição por tipo de participante SCD; e se a Conta PI do
  ISPB 46026562 está provisionada no homolog). NÃO afirmar isso sem a prova do xmlsec1 acima.
Outras ideias: comparar BYTE-A-BYTE a assinatura de uma msg que o BACEN aceitou (pibr.001) vs a
camt.060 (mesmo signer, Document diferente); ver se camt.060 exige sessão ICOM estabelecida.

## REVISÃO FULL pedida pelo dono — TODAS as mensagens
Builders que geramos do zero: spi_client.ex `build_camt060_request`, `build_pibr001_request`
(este BATE com o exemplo — referência de "certo"), `build_admi004_request`. pacs.008/pacs.002/
pacs.004/pain.* recebem `xml_body` pronto (achar o MessageBuilder/origem e revisar). Método: p/
CADA tipo, gerar nosso XML, `xmllint --schema xsd/<msg>` + diff normalizado contra `exemplos/<msg>/*.xml`,
listar divergências + corrigir. Priorizar os que enviamos: pacs.008, pacs.002, pacs.004, camt.060,
pibr.001(ok), admi.004, pain.*. (DICT GetEntry já funciona, baixa prioridade.)

## Tooling (reusável)
`scratchpad/runrpc.sh <arquivo.exs>` — base64 -> /tmp no container -> `bin/monetarie_pix rpc
Code.eval_file(~s(/tmp/x.exs))` no node VIVO (Repo up). Resolve task RUNNING. Container tem xmllint +
XSDs em `/app/lib/shared-0.1.0/priv/xsd/spi/v5.12.1/`. Conta 990933657879, profile vulcimonetarie,
service `pix-api`, cluster monetarie-greenfield-homolog.

## CONSTRAINT vivo
Sessão PIX ativa em LOOP de deploy da pix-api (vi :34->:35->:36->:37 em ~2h) buildando do WORKING
TREE com 4 arquivos não-commitados (dict_lookup_responder, core_event_processor, dict_client,
e2e_cache). NÃO editar o working tree (entraria no build dela) nem buildar/deployar a pix-api sem
coordenar. Fazer correções em git worktree isolado. Front pix-admin-ui builda separado.
```
