# Construtor de Mensagens vs catálogo SFN/SPI v5.12.1 — plano de implementação

> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans (ou subagent-driven-development) para implementar task a task.

**Goal:** alinhar catálogo de campos, whitelist de params, builders XML e formulário do PIX admin com o catálogo SFN/SPI v5.12.1, campo a campo, com prova por teste (sentinela + XSD) para cada mensagem.

**Architecture:** fonte única de verdade nova (`SettlementServiceWeb.MessageFormCatalog`) com os campos por tipo + metadados de campo (`field_meta`: tipo de input, domínio de select, obrigatoriedade). O `@known_params` do controller passa a ser DERIVADO do catálogo (mata a classe de bug "campo descartado em silêncio"). Guardas de paridade em teste: (1) todo campo do catálogo sobrevive ao `convert_params`; (2) todo campo do catálogo afeta o XML gerado (teste sentinela por tipo); (3) XML totalmente preenchido valida contra o XSD v5.12.1 (XsdValidator/xmllint). UI renderiza select quando `field_meta` traz domínio; ISPB usa a store de participantes já existente.

**Tech Stack:** Elixir (settlement_service + shared), Vue 3 (frontend/admin), XSDs em `pix/backend/apps/shared/priv/xsd/spi/v5.12.1/`.

**Base de fatos:** `docs/reports/2026-07-06-construtor-mensagens-matriz-gaps-catalogo.md` (matriz exaustiva com file:line). Testes locais: `DB_PASS=monetarie mix test` (PG local porta 15432). 4 falhas PRÉ-existentes na suíte shared (3 reda namespace + 1 dict_headers) não são regressão.

---

### Task 1: Fonte única `MessageFormCatalog` + whitelist derivado + guarda de paridade

**Files:**
- Create: `pix/backend/apps/settlement_service/lib/settlement_service_web/message_form_catalog.ex`
- Modify: `pix/backend/apps/settlement_service/lib/settlement_service_web/controllers/message_controller.ex` (`@known_params:46-67`, `convert_params/1:861`, `message_type_catalog/0:1041-1339`, parity guard `:1341`)
- Test: `pix/backend/apps/settlement_service/test/settlement_service_web/message_form_catalog_test.exs`

Passos (TDD): teste RED de que `MessageFormCatalog.known_params/0` contém todo campo de `required_fields`+`optional_fields` de todos os tipos e que `MessageController` usa esse conjunto no `convert_params` (campo do catálogo NUNCA é dropado). Mover o catálogo do controller para o módulo novo (conteúdo idêntico nesta task; correções de campos vêm nas tasks seguintes). `@known_params` legado vira união (catálogo ∪ extras usados por outros fluxos). GREEN, suíte settlement_service, commit.

### Task 2: pacs.008 completo

**Files:** catálogo (task 1), `shared/lib/shared/bacen/iso20022/message_builder.ex:159-263`, test novo `shared/test/shared/bacen/iso20022/message_builder_pacs008_catalog_test.exs`.

Campos: required = debtor_ispb, creditor_ispb, amount, debtor_name, debtor_document, debtor_account, debtor_account_type, creditor_document, creditor_account, creditor_account_type, purpose; optional = debtor_branch, creditor_branch, priority, local_instrument, proxy_key, end_to_end_id, tx_id (creditor_name REMOVIDO: Cdtr é IdPrivateIdentification sem Nm). Builder: conferir que lê priority (`p[:priority]` default HIGH), purpose (`p[:purpose]` default IPAY), local_instrument; adicionar leituras que faltarem. Teste sentinela: build com todos os campos preenchidos com valores válidos distintos e assert de cada um no XML (CPF em `Dbtr/Id/PrvtId/Othr/Id`, agência em `Issr`, `Tp/Cd`, `Purp/Cd`, `InstrPrty`). Teste XSD: XML completo valida contra `pacs.008.spi.1.15.xsd` (usar `Shared.Validation.XsdValidator`). Commit.

### Task 3: pacs.004

Campos: required = original_end_to_end_id, return_amount, return_reason (select BE08/FR01/MD06/SL02), debtor_ispb, creditor_ispb; optional = priority. Builder `:325-350` já lê debtor/creditor_ispb — garantir emissão em `OrgnlTxRef`. Sentinela + XSD (`pacs.004.spi.*.xsd`). Commit.

### Task 4: trck.002

Whitelist: garantir execution_datetime, debtor_id, creditor_id, debtor_account_type, creditor_account_type, local_instrument, payment_scenario via catálogo. Catálogo atualizado. Sentinela + XSD `trck.002`. Commit.

### Task 5: reda.016/017/041 builders mínimos

`message_builder.ex` `build_document/2` ganha cláusulas para os 3 (XSD v5.12.1 correspondente; campos do catálogo alinhados aos elementos obrigatórios). RED: build hoje dá raise "Unknown message type". GREEN: XML mínimo válido por XSD. Nota: 3 testes pré-existentes de namespace reda falham no ambiente local (não relacionados); não tocar neles. Commit.

### Task 6: pain.009/011/012/013/014

- pain.011: name/description corrigidos para Cancelamento (builder é MndtCxlReq); campos = original_mandate_id (req), cancellation_reason (opt, lido pelo builder).
- pain.012: name/description = MandateAcceptanceReport; campos = accepted (select true/false), mandate_status, from_ispb; remover campos mortos.
- pain.009: expor mandate_id, creditor_name, creditor_document, debtor_document, debtor_account, creditor_ispb, debtor_ispb, frequency (select MNTH etc.).
- pain.013: expor requested_execution_datetime (não `execution_date`), amount, creditor/debtor_document, creditor/debtor_ispb; pain.014: original_payment_info_id (não `original_msg_id`), status/reason_code selects.
Sentinela por tipo. Commit.

### Task 7: camt.014/053/054/055/060

- camt.014: campos = member_ispb, member_name, member_cnpj, member_type, member_status, original_msg_id (todos lidos pelo builder).
- camt.053: remover from_date/to_date do required (builder não usa) OU passar a usá-los (decidir pelo XSD; `FrToDt` existe? conferir) — o que o XSD suportar.
- camt.054: remover notification_id (builder gera) ou passar a usar; expor debtor_ispb/creditor_ispb; direction/entry_status como select.
- camt.055: original_msg_id → mapear para o que o builder lê (payment_cancellation_id/original_payment_info_id) ou builder passa a ler original_msg_id; cancellation_reason select.
- camt.060: builder lê `p[:balance_type]` (hoje lê `p[:prtry]`) — alinhar; select CSA/REL/TRD/CRE.
Sentinela por tipo. Commit.

### Task 8: pibr.002/admi.002/admi.004/reda.022

- pibr.002: campo = original_data (builder lê); remover original_msg_id morto. admi.002: alinhar a original_msg_id/event_description/reason_description (o que o builder lê); remover event_code morto ou builder passa a lê-lo.
- reda.022: controller monta `modifications` estruturado a partir dos 10 campos de contato/diretor do form ANTES de chamar o builder (função pura testável `reda022_modifications/1`).
Sentinela. Commit.

### Task 9: `field_meta` no catálogo (domínios de select)

Estende `MessageFormCatalog`: `field_meta` por campo: `%{type: :text|:number|:date|:datetime|:select, options: [...], source: :participants}`. Domínios: account_type CACC/OTHR/SLRY/SVGS/TRAN; purpose GSCB/IPAY/IPRT/OTHR/REFU; priority HIGH/NORM; local_instrument APDN/APES/AUTO/DICT/INIC/MANU/QRDN/QRES; status pacs.002 ACCC/ACSC/ACSP/RJCT; return_reason BE08/FR01/MD06/SL02; frequency MNTH/...; direction CRDT/DBIT; entry_status BOOK; balance_type CSA/REL/TRD/CRE; campos `*_ispb` → `source: :participants`. `GET /api/v1/messages/types` devolve field_meta (aditivo). Teste do payload. Commit.

### Task 10: Frontend MessageBuilderView

**Files:** `pix/frontend/admin/src/views/messages/MessageBuilderView.vue`, `stores/message.ts`, labels `:37-89`.

- Renderizar `<select>` quando field_meta.type==select (options do payload); `source: participants` → options da `useParticipantsStore` (`ispb — nome`), carregar on-mount.
- Labels pt-br para os campos novos (debtor_document "CPF/CNPJ do Devedor", debtor_branch "Agência do Devedor", *_account_type "Tipo de Conta ...", purpose "Finalidade", priority "Prioridade", etc.).
- `npm run build` verde. Commit.

### Task 11: suíte de exatidão XSD (todas as OUTBOUND)

Teste por tipo OUTBOUND do catálogo: build com params completos → `XsdValidator` valida contra o XSD v5.12.1. Registrar exceções documentadas (se um builder emite versão diferente, corrigir builder, não o teste). Commit.

### Task 12: deploy + validação viva + evidência

Build arm64 pix-api tag imutável, task-def novo, rollout COMPLETED, health 200. Validação regra 11: screenshots do form pacs.008 com selects e campos novos, pibr.001 pelo construtor (já validado o agrupamento), envio pacs.008 com XML conferido. Atualizar handoff + CLAUDE.md + memória. Commit/push.
