# Dossie de paridade PROFUNDO — reda.016 / reda.017 / reda.022 / reda.041 (dados de referencia / participante)

Auditor: auditor senior de sistemas PIX. Metodo: read-only, prova arquivo:linha (legado decompilado + DB vivo + scripts) x prova arquivo:linha (nosso Elixir/Vue/DB). Sem inferencia: onde falta prova, o veredito e INCONCLUSIVO.

Escopo do catalogo REDA (dados de referencia / participante), com as descricoes oficiais do proprio legado (`SPI.Core.General.decompiled.cs:8155` enum `enumTipoMsgDescricao`):

| Msg | Descricao legado | Sentido | XML raiz |
|-----|------------------|---------|----------|
| reda.014 | REGISTRA PARTICIPANTE INDIRETO | outbound (nos -> BACEN) | `PtyCreReq` |
| reda.016 | RETORNO DO REGISTRO DO PARTICIPANTE | inbound (BACEN -> nos) | `PtyStsAdvc` |
| reda.017 | AVISO PRAZO SPI | inbound | `PtyRpt` |
| reda.022 | ATUALIZA RESPONSAVEL | outbound | `PtyModReq` |
| reda.031 | DESREGISTRA PARTICIPANTE INDIRETO | outbound | `PtyDeltnReq` |
| reda.041 | ATUALIZACAO DE PARTICIPANTE | inbound | `PtyActvtyAdvc` |

O pedido pede foco em 016/017/022/041, mas 014/031 sao inseparaveis do ciclo (016 e a resposta de 014/022/031; 017 amarra em 031). Cobrimos todos.

---

## 1. LEGADO — comportamento com prova

### 1.1 Enum e catalogo de mensagens
- Enum canonico com os 6 REDA: `SPI.Core.General.decompiled.cs:8111-8122` (`REDA014..REDA041`, `[Description("REDA.0xx")]`).
- Descricoes de negocio: `SPI.Core.General.decompiled.cs:8181-8192`.
- `enumIssuer` default = `BCB`: `SPI.Core.General.decompiled.cs:7805` + usos `:10928/:11006/:11058` (`Issuer = enumIssuer.BCB`).

### 1.2 DownloadAutomatico — diretorio de participantes SPI (a "pista")
Worker `DownloadAutomaticoUseCase` — `SPI.Core.Worker.DownloadAutomatico.Application.decompiled.cs:73-370`:
- `ExecutaVerificacaoEDownload` (L114) chama, em sequencia, `DownloadCertificado` (L129), `DownloadParticipante` (L183), `DownloadBanco` (L275). Loop de servico com pausa minima 60s (`SPIConfigDownloaAutomatico` L421-433).
- `DownloadParticipante` (L183-273): so roda `DateTime.Now.Hour >= 6` e a cada 24h (L188). Le o parametro `DownloadParticipantes` (L192); baixa a URL com `AAAAMMDD` substituido pela data (L197); parseia CSV separado por `;` (L230). Regra por linha (L231-252): `listCampos.Length > 4 && listCampos[0] != "participante"`; campos: `[0]`=ISPB (PadLeft 8), `[2]`=nome completo, `[3]`=nome curto, `[5]`=tipo (`"DRCT"` => `Direto`, senao `Indireto`). Upsert em `Participante` (`FindPorISPB` -> inclusao ou alteracao se nome/tipo/status mudou) via `AtualizaListaParticipante` (L255).
- `DownloadBanco` (L275-369): CSV separado por `,`; campos `[0]`=ISPB, `[1]`=nome, `[2]`=codigo COMPE (<=3, numerico); upsert em `Banco`.
- `DownloadCertificado` (L129-181): baixa zip de certificados QR (certqrc), extrai no diretorio, 24h.
- Config viva (DB `crk_spi.dbo.SpiCadParam`, `IdParam LIKE 'Download%'`):
  - `DownloadParticipantes` = `{"Ativa":true,"URL":"http://www.bcb.gov.br/content/estabilidadefinanceira/spi/participantes-spi-AAAAMMDD.csv"}` (arquivo **SPI**).
  - `DownloadBancos` = `{"Ativa":true,"URL":".../ParticipantesSTRport.csv"}` (arquivo **STR**).
  - `DownloadCertificados` = `{"Ativa":false,"URL":".../certqrc-h.zip",...}`.

### 1.3 Inbound REDA — materializacao (TrataRetornoLegado)
Catalogo `SPI.Core.Application.decompiled.cs:5400-5438` (`lstConversors`, roteia por `MsgDefIdr`/CdMsg com vigencia por data):
- reda.014 -> `IRetornoLegadoWriteRepository<ParticipanteIndireto>` (L5415-5417).
- reda.031 -> `ParticipanteIndireto` (L5419-5420).
- **reda.017 -> `PrazoDesvinculacaoParticipanteIndireto`** (L5423-5424).
- **reda.041 -> `Participante`** (L5427-5428).
- **reda.016 NAO esta no catalogo** (nao ha handler de materializacao inbound).

Handlers (traducao XML -> entidade):
- **reda.041** `Handlers.REDA041.v10` (L5537-5578): itera `*/Document/PtyActvtyAdvc/PtyActvty`; ISPB de `Chng/PtyId/Id/Id/PrtryId/Id`; `Tipo=Direto`, `Status=Ativo`; nome curto do 1o `Rcrd/Nm/New/Nm`, nome completo do 2o. Grava/upsert em `Participante` (o diretorio geral). Cria participante novo se nao existir (write repo).
- **reda.017** `Handlers.REDA017.v10` (L5608-5638): itera `*/Document/PtyRpt/RptOrErr/PtyRpt`; monta `PrazoDesvinculacaoParticipanteIndireto` com `IdInstFinanc`, `DtHrEntrada = DtHrMsg`, `IspbParticipante` de `PtyId/.../PrtryId/Id`, **`DtHrLimite` de `PtyOrErr/SysPty/MktSpcfcAttr/Val`** (nao filtra por `Nm`), **`Issuer` de `PtyId/.../PrtryId/Issr`**, `Desvinculado=false`.
- reda.014 `Handlers.REDA014.v10` (L5640-5667): do `JsonInput` (a requisicao original) so materializa `ParticipanteIndireto` (Ativo) **se `IdStatus.EhTerminoOK()`** — ou seja, o desfecho terminal vem da correlacao do protocolo (a resposta reda.016 setando IdStatus), nao de um handler reda.016.
- reda.031 `Handlers.REDA031.v10` (L5580-5606): `ParticipanteIndireto` (Inativo) se terminado OK.

### 1.4 Consumo do prazo de desvinculacao (017 amarra em 031)
- Ao desregistrar (reda.031), o SQL faz `update SpiPartIndireto set IcAtivo=@IcAtivo ...` **E** `update SpiPendDesvPartIndireto set IcPendente=1 where IdInstFinanc=@IdInstFinanc and IspbParticipante like @DsCnpjParticipante+'%' and IcPendente=0` — `SPI.Core.Infrastructure.decompiled.cs:3385`. Ou seja, o prazo materializado por reda.017 e um ESTADO rastreado que fecha quando a desvinculacao ocorre.
- Repositorio dedicado com `List(idInstFinanc, ispb, bDesativado)`, `InsereRepr` (reapresentacao idempotente por `IdInstFinanc+IspbParticipante`), `Desvinculado` (`SPI.Core.Infrastructure.decompiled.cs:3609-3690`; entidade `SPI.Core.Domain.decompiled.cs:1007`).

### 1.5 Outbound REDA — validacao + montagem
- Validacoes: `ValidaREDA014UseCase` (`SPI.Core.Application.decompiled.cs:4614-4652`), `ValidaREDA022UseCase` (L4654-4744), `ValidaREDA031UseCase` (L4746+). reda.022 valida `Contato` (Fone obrig, Email obrig, Celular/Fax opc — L4733-4744), `Diretor` (Nome/Fone/Email obrig, Celular opc — L4720-4731), `PalavraChave`/`TecAddr` (8 alfanum obrig — L4704-4718), `CpfDiretorResp` (CPF obrig — L4692). reda.014 valida CNPJ obrig, `ParticipanteIndiretoJaExistePorISPB` (nao existe), `NmParticipante` obrig <=100 (L4640-4642); `IspbCreditor="00038166"` (L4646).
- Montagem (`SPI.Core.Mensageria.Application.Book.v111.decompiled.cs`): reda.022 `PtyModReq` com `SysPtyId` de `msg.IspbDebtor` + `Issr=Issuer.Descricao()`, e 4 `Mod` (CtctDtls contato, CtctDtls diretor com `Nm`, `TechAdr/TechAdr`, `MktSpcfcAttr Nm/Val`) — `:600-760`; `ScpIndctn` = `TipoAlteracao.Descricao()` POR modificacao (nao fixo). reda.031 `PtyDeltnReq` com `Issr=Issuer.Descricao()` (`:377/:471`).

### 1.6 Traducao/exibicao (Angular API)
`SPI.Core.Angular.Application.decompiled.cs` roteia por CdMsg (`:681-686`) e traduz cada REDA para exibicao:
- reda.014 `TraduzREDA014` (L1372): `PtyCreReq/...` (participante direto, tipo, `MktSpcfcAttr Nm/Val`=CNPJ).
- **reda.016 `TraduzREDA016` (L1387)**: `PtyStsAdvc` -> `MsgHdr/OrgnlBizInstr/MsgId` (id original), `PtySts/Sts` (situacao), `PtySts/StsRsn/Rsn/Prtry` (codigo erro), `SysPtyId/.../Id` (participante), `RspnsblPtyId/.../Id` (direto). Somente exibicao.
- **reda.017 `TraduzREDA017` (L1404)**: `PtyRpt/RptOrErr/PtyRpt/...`; rotulo do valor = **"LIMITE CONFIRMACAO ENCERRAMENTO"** (`MktSpcfcAttr/Val`).
- **reda.022 `TraduzREDA022` (L1418)**: `PtyModReq` + N `Mod` (ScpIndctn, CtctDtls Nm/PhneNb/MobNb/FaxNb/EmailAdr/Rspnsblty, TechAdr, MktSpcfcAttr).
- **reda.041 `TraduzREDA041` (L1460)**: `PtyActvtyAdvc/PtyActvty/Chng` + N `Rcrd/Othr` (`FldNm`/`OdFldVal`/`NewFldVal`).

### 1.7 Persistencia legado (DB vivo `CRK_SPIDOMINIO`)
- `SpiParticipante` (72.846 linhas): `IspbParticipante char(8)`, `NmParticipante varchar(255)`, `IcAtivo int`, `IdTipoParticipante int`, `NmCompletoParticipante varchar(500)`. Distribuicao `IdTipoParticipante`: 1=72.837, 2=9. Fonte dupla: DownloadParticipante + reda.041.
- `SpiBanco` (368): `CdBanco char(3)`, `NmBanco varchar(150)`, `Ispb char(8)`. Fonte: DownloadBanco.
- `SpiPartIndireto` (7): `IdInstFinanc int`, `DsCnpjParticipante char(14)`, `NmParticipante varchar(255)`, `IcAtivo int`. Fonte: reda.014/031.
- `SpiPendDesvPartIndireto` (0): `IdInstFinanc int`, `IspbParticipante varchar(14)`, `DtHrEntrada datetime2`, `DtHrLimite datetime2`, `Issuer int`, `IcPendente tinyint`. Fonte: reda.017; fechado por reda.031.

---

## 2. NOSSO — comportamento com prova

### 2.1 Ciclo outbound reda.014/022/031 (participante indireto)
- Contexto `Shared.Reda` (`apps/shared/lib/shared/reda.ex`): `list/count/get/create/update_responsibles/mark_status`; idempotencia por `client_request_id`; recuperacao de REJECTED por reset a PENDING (L79-112). Estados `PENDING ACTIVE REJECTED DEREGISTERING INACTIVE` (schema L13).
- Schema `Shared.Schemas.Reda.IndirectParticipant` (`apps/shared/lib/shared/schemas/reda/indirect_participant.ex`): tabela `monetarie_settlement.indirect_participants`; `create_changeset` (L41-54): `indirect_ispb` `\d{8}`, `indirect_cnpj` **alfanumerico** `[0-9A-Z]{12}[0-9]{2}` (IN RFB 2.229), `name` <=100, unique ISPB + client_request_id. `update_responsibles_changeset` (L56-80): director_name/cpf(`\d{11}`)/phone/email + contact_phone/email + tech_keyword(`[A-Za-z0-9]{8}`) TODOS obrigatorios; emails validados.
- Controller `SettlementServiceWeb.Admin.RedaController` (`apps/settlement_service/.../admin/reda_controller.ex`): `POST` -> reda.014, `PUT` (so ACTIVE) -> reda.022, `DELETE` (so ACTIVE, marca DEREGISTERING) -> reda.031; fail-closed 502 `send_failed` mantendo status pre-envio (L141-159); RBAC por acao (L28-33); `history` correlaciona `bacen_outbound` por ISPB no XML + `bacen_inbound` reda.016 (L174-237). Rotas `router.ex:489-493`.
- Sender `Shared.Reda.Sender` (`apps/shared/lib/shared/reda/sender.ex`): mapeia row+op -> params, gera `BizMsgIdr`, `MessageBuilder.build/2`, assina no HSM (`XmlSigner.sign_spi/2`) e envia por `Client.spi_request` (canal CSM/nao-financeiro auto-roteado); grava `bacen_outbound`. reda.022 usa `party_ispb=indirect_ispb` + 4 mods (L103-138).
- Builder `Shared.Bacen.Iso20022.MessageBuilder`: `build_reda014` (`message_builder.ex:842-875`, `Tp/Prtry=IDRT`, `MktSpcfcAttr Nm=CNPJIDRT`, `Issr=BCB`); `build_reda022` + `reda022_modifications` + `build_reda022_mod` (L1357-1502, 4 Mods CONTATOPSP/DIRETORPSP/TechAdr/`MktSpcfcAttr Nm=CPFDIRETOR`, `ScpIndctn=INSE` fixo); `build_reda031` (`PtyDeltnReq`, `Issr=BCB`).

### 2.2 Inbound reda.016 — status advice (resposta a 014/022/031)
- `InboundProcessor.process_party_status_advice` (`apps/spi_service/lib/spi_service/workers/inbound_processor.ex:214,2076-2214`): enriquece campos do XML (`enrich_reda016_from_xml`, L2129), correlaciona por `original_msg_id` -> `Shared.Reda.get_by_request_msg_id` -> maquina de estado por `Sts` (COMP/QUED/REJT, XSD `reda.016.spi.1.5.xsd`): PENDING(014)->REJECTED em REJT; ACTIVE(022) permanece ACTIVE; DEREGISTERING(031)->ACTIVE em rejeicao, COMP fecha (L2144-2213). Tambem ha correlacao via admi.002 espelhando o mesmo state machine (L1804-1925).
- Parser `MessageParser.extract_party_status_advice` (`message_parser.ex:321-370`).
- **Diferenca vs legado: temos handler DEDICADO de materializacao inbound de reda.016; o legado nao tem (so exibicao) — o legado fecha o ciclo pela correlacao generica do protocolo (IdStatus.EhTerminoOK).**

### 2.3 Inbound reda.017 — PtyRpt (prazo)
- `InboundProcessor.process_party_report` (`inbound_processor.ex:215,2216-2273`): `MessageParser.extract_party_report` (`message_parser.ex:530-542`) le ISPB de `RptOrErr/.../PrtryId/Id` e `confirmation_deadline` de `MktSpcfcAttr Nm=PRAZOCONFI/Val` (scoped); `Shared.Reda.apply_party_report` (`reda.ex:144-161`) faz `Repo.get_by(indirect_ispb)` e grava `confirmation_deadline` (uma coluna). Se participante nao existe local -> `{:error,:not_found}` + so audit trail; grava inbound + broadcast `party.report`.
- Coluna `confirmation_deadline timestamptz` aditiva nullable (`migrations/20260717233000_add_confirmation_deadline_to_indirect_participants.exs`; moduledoc: "prazo de confirmacao da migracao do PI").

### 2.4 Inbound reda.041 — PtyActvtyAdvc (alteracao cadastral)
- `InboundProcessor.process_party_activity_advice` (`inbound_processor.ex:216,2275-2335`): `MessageParser.extract_party_activity_advice_changes` (`message_parser.ex:557-576`) le ISPB de `Chng/.../PrtryId/Id` e as N `Rcrd/Othr` (`FldNm`/`OdFldVal`/`NewFldVal`, MODP/NOME/NOMR); `Shared.Reda.apply_activity_advice` (`reda.ex:171-190`) faz `Repo.get_by(indirect_ispb)` e aplica **SO o campo `NOME`** (`row.name`). Sem participante local -> `{:error,:not_found}` + audit; grava inbound + broadcast.

### 2.5 Diretorio de participantes (equivalente ao DownloadParticipante)
- `Shared.Bacen.PixParticipants.ImportWorker` (Oban, fila `bacen_reference`) + `DirectoryImporter` (`apps/shared/lib/shared/bacen/pix_participants/directory_importer.ex`): baixa **`lista-participantes-instituicoes-em-adesao-pix-YYYYMMDD.csv`** (CSV; fallback PDF), delete+insert por `source_date` em `monetarie_spi_ref.bacen_pix_participants`, e upsert em `monetarie_spi_ref.banks` (L132-160). Cron diario `{"30 9 * * *", ...}` = 06:30 BRT (`config/config.exs:199`).
- CSVParser (`csv_parser.ex`): separador `;`, 11 colunas incl. `spi_participation_type`, `pix_participation_type`, `pix_participation_modality`, ISPB, CNPJ, `authorized_by_bcb`, `payment_initiator`, `fss`. Schema `BacenPixParticipant` (`schemas/spi_ref/bacen_pix_participant.ex`) captura tudo isso; snapshot por data.
- **A reda.041 NAO alimenta esse diretorio** (so `indirect_participants`).

### 2.6 XSDs e testes
- Parser identifica REDA por raiz XML (`message_parser.ex:118-123`: PtyCreReq/PtyStsAdvc/PtyRpt/PtyModReq/PtyDeltnReq/PtyActvtyAdvc).
- Testes: `apps/shared/test/shared/reda_test.exs`, `.../reda/builder_xsd_test.exs`, `.../schemas/reda/indirect_participant_test.exs`, `message_builder_reda_inbound_catalog_test.exs`, `message_parser_reda_extracts_test.exs`, `apps/spi_service/test/.../inbound_processor_reda017_041_test.exs`, `inbound_processor_reda_test.exs`, `outbound_validation_test.exs`, `apps/settlement_service/test/.../admin/reda_controller_test.exs`.

---

## 3. GAPS e COBERTOS (legado x nosso)

### COBERTOS
- **C1 reda.014 outbound** — build+valida+assina+envia+ciclo. Nosso: `reda.ex:79`, `sender.ex:99-101`, `message_builder.ex:842`; legado: `ValidaREDA014UseCase` L4614 + MontaMsg v111. CNPJ obrig, nome <=100, ISPB unico. Nosso aceita CNPJ ALFANUMERICO (IN RFB 2.229) — mais atual que o `ValidaCNPJ` numerico do legado.
- **C2 reda.022 outbound** — 4 Mods (CONTATOPSP/DIRETORPSP/TechAdr CPFDIRETOR) batem com XSD reda.022.spi.1.4 e com o template v111 do legado (`:600-760`). Validacoes obrigatorias equivalentes (`indirect_participant.ex:56-80` x `ValidaREDA022UseCase` L4704-4744).
- **C3 reda.031 outbound** — `PtyDeltnReq` (`message_builder.ex build_reda031`) x MontaMsg v111 (`:377`).
- **C4 reda.016 inbound (state machine)** — correlacao + COMP/QUED/REJT dirigindo PENDING/ACTIVE/DEREGISTERING (`inbound_processor.ex:2076-2213`). Legado nao tem handler dedicado; nosso e >= legado (fecha o ciclo de 014/022/031).
- **C5 diretorio de participantes por download diario** — `ImportWorker`+`DirectoryImporter` (cron 06:30 BRT) x `DownloadParticipante` (>=6h, 24h). Ambos capturam tipo de participacao SPI (nosso via `spi_participation_type`).
- **C6 parse/exibicao de reda.016/017/022/041** — parser + `history` do controller x `TraduzREDAxxx` do Angular.

### GAPS
- **G1 (divergencia, medio) reda.041: escopo do alvo.** Legado upserta o DIRETORIO GERAL `SpiParticipante` (qualquer participante; INSERE novo; nome curto+completo) — handler L5537-5578. Nosso so aplica ao proprio `indirect_participants` por `get_by(indirect_ispb)` e SO o campo NOME (`reda.ex:171-190`); ISPB fora da nossa base = audit-only. Entre downloads diarios, alteracao cadastral do BCB de um participante direto nao entra no diretorio.
- **G2 (divergencia, medio) reda.017: modelo de materializacao.** Legado = tabela dedicada `SpiPendDesvPartIndireto` com `DtHrEntrada`, `DtHrLimite`, `Issuer`, flag `IcPendente`, reapresentacao idempotente e **fechamento por reda.031** (`Infrastructure:3385`). Nosso = 1 coluna `confirmation_deadline` sobrescrita no participante (`reda.ex:144-161`), sem Issuer, sem timestamp de entrada, sem flag pendente, sem historico e SEM amarracao com reda.031.
- **G3 (parcial, baixo) reda.017: pre-condicao de existencia.** Legado insere o prazo mesmo sem `SpiPartIndireto` (chave IdInstFinanc+IspbParticipante). Nosso so materializa se o indireto ja existe local; senao `{:error,:not_found}` (audit-only) — `reda.ex:146-147`, `inbound_processor.ex:2238-2242`.
- **G4 (parcial, baixo) reda.041: campos aplicados.** Nosso aplica SO `FldNm=="NOME"` (`reda.ex:178`); ignora MODP e NOMR (nome completo/razao). Nao ha coluna de nome completo no indireto (legado tinha `NmCompletoParticipante`).
- **G5 (divergencia, baixo) reda.022: ScpIndctn.** Nosso fixa `ScpIndctn=INSE` nas 4 Mods (`message_builder.ex:1447/1463/1479/1491`). Legado usa `TipoAlteracao.Descricao()` POR Mod (`v111:600-760`) — suporta escopos distintos (ex.: remocao de um contato). Nosso nao consegue expressar isso.
- **G6 (parcial, info) download de bancos STR e certificados QR.** Legado `DownloadAutomatico` baixa arquivo STR separado (`ParticipantesSTRport.csv` -> `SpiBanco`) e zip de certificados QR (`certqrc`). Nosso deriva `banks` como efeito colateral do import PIX (sem arquivo STR proprio) e os certs QR sao tratados fora do diretorio. Referencia de bancos coberta por outro caminho; sem paridade 1:1 do worker.
- **G7 (parcial, baixo) reda.014/022: contexto de validacao multi-instituicao.** Legado valida InstFinanc/Filial/OriginSystem/IspbDebtor (`ValidaREDA0xx`). Nosso (mono-tenant) valida formato+unicidade. Risco baixo no modelo atual, mas a checagem "nao existe por ISPB" do legado equivale ao nosso `unique_constraint` (coberto).

### INFO (nao-gap, registrado para evitar falsa divergencia)
- **I1 reda.017: rotulo semantico.** Angular do legado rotula o `MktSpcfcAttr/Val` como "LIMITE CONFIRMACAO ENCERRAMENTO" (desvinculacao) e o handler grava em `PrazoDesvinculacao...`; nosso rotula `PRAZOCONFI` = "prazo de confirmacao da migracao do PI". Ambos leem o MESMO `MktSpcfcAttr/Val` (legado nao filtra por Nm; nosso escopa Nm=PRAZOCONFI). Diferenca de nomeacao/uso do prazo, mesma origem de dado — nao afirmo que sejam prazos regulatoriamente distintos sem a spec BACEN vigente (INCONCLUSIVO sobre a semantica exata, CERTO sobre a origem do valor).
