# Dossiê de paridade PROFUNDO — pacs.002 (status) e pacs.028

Auditoria READ-ONLY. Escopo: máquina de status do pacs.002 (ACSP/ACCC/ACSC/STLD/RJCT),
consulta de status (pacs.028) e a ordem de autoridade de liquidação (pacs.002 é a verdade
que credita; camt.060 reconcilia preso; camt.054 BOOK é fallback de último recurso).
Cada afirmação tem prova arquivo:linha (decompilado + Elixir) ou tabela.coluna (DB vivo) ou
script SQL. Sem prova = verdict INCONCLUSIVO. pt-br sem travessão.

Fontes:
- Legado decompilado: `.scratch/legado-pix-decompiled/*.decompiled.cs`
- Legado DB vivo (SQL Server): `crk_spi`, `CRK_SPIDOMINIO`
- Legado scripts SQL: `LegadoPIX/Pix/SPI/Database-Inicial/.../Procedures`
- Nosso backend: `pix/backend/apps/{spi_service,shared,settlement_service}`
- Nosso frontend: `pix/frontend/admin/src`

---

## 1. LEGADO — como o pacs.002 vira status (com prova)

### 1.1 O mapa de status é DIRIGIDO POR TABELA (de-para no banco)

O legado NÃO tem `if TxSts == "ACSP"` espalhado. Ele lê uma tabela de de-para
`SpiStatus` (schema `CRK_SPIDOMINIO`), chaveada por `(IdRefSource, TxSts)`.

Prova (mapeamento EF): `SPI.Core.Mensageria.Geral.Infrastructure.decompiled.cs:1476-1495`
(`class StatusCodeXStatusOperacaoConfiguration` → `eb.ToTable("SpiStatus")`,
`eb.HasKey(b => new { b.RefSource, b.TxSts })`, coluna `IdStatusOperacao`).

Prova (conteúdo VIVO da tabela `CRK_SPIDOMINIO.dbo.SpiStatus`):

| IdRefSource | TxSts | NmSts | IdStatusOperacao |
|---|---|---|---|
| **6** | **ACCC** | AcceptedSettlementCompleted | **9 (Efetivada)** |
| **6** | **ACSC** | AcceptedSettlementCompletedDebitorAccount | **9 (Efetivada)** |
| **6** | **ACSP** | AcceptedSettlementInProcess | **11 (PendenteVerificacaoCredito)** |
| **6** | **RJCT** | Rejected | **10 (Rejeitada)** |
| 7 | COMP | Completed | 9 (Efetivada) |
| 7 | QUED | Queued | 8 (EmProcessamento) |
| 7 | REJT | Rejected | 10 (Rejeitada) |
| 31 | ACCR | AcceptedCancellationRequest | 9 |
| 31 | RJCR | RejectedCancellationRequest | 10 |
| 33 | (vazio) | Rejeitada | 10 |
| 33 | CCLD | Cancelada | 9 |
| 33 | CFDB | Confirmada | 9 |
| 33 | PDNG | Pendente | 7 |

`enumRefSource` (`SPI.Core.General.decompiled.cs:7958-7969`): **6 =
ExternalPaymentTransactionStatus1Code** (é o codeset TxSts do pacs.002), 7 = Status6Code,
33 = statusRecorrencia. O RefSource 31 (camt.029 cancelamento) não está no enum mas está na
tabela.

`enumStatusOperacao` (`SPI.Core.General.decompiled.cs:7114-7216`): **9 = Efetivada**,
**10 = Rejeitada**, **11 = PendenteVerificacaoCredito**, 7 = AguardandoRetorno,
8 = EmProcessamento.

**Consequência (crítica) para o pacs.002 (RefSource 6):**
- ACCC → 9 Efetivada (TERMINAL OK)
- ACSC → 9 Efetivada (TERMINAL OK)
- **ACSP → 11 PendenteVerificacaoCredito (NÃO TERMINAL)** — o aceite em processamento NÃO
  liquida; a operação segue aberta esperando ACCC/ACSC ou camt.054 BOOK.
- RJCT → 10 Rejeitada (TERMINAL ERRO)
- **STLD NÃO EXISTE no de-para do RefSource 6** (o legado não recebe/mapeia STLD como TxSts
  de pacs.002).

Prova de que 9/10 são terminais e 11 não é (join `SpiStatusOperacao` × `SpiClassifResultado`,
DB vivo `crk_spi`):

| IdStatus | Resultado | EhStatusFinal | EhErro |
|---|---|---|---|
| 9 (Efetivada) | 2 | **S** | N |
| 10 (Rejeitada) | 3 | **S** | S |
| 11 (PendenteVerificacaoCredito) | 0 | **N** | N |
| 7 (AguardandoRetorno) | 0 | N | N |
| 8 (EmProcessamento) | 0 | N | N |

### 1.2 Onde o de-para é aplicado

`InterpretaConteudo(refSource, dominio)` (`SPI.Core.Mensageria.Geral.Application.decompiled.cs:3020-3049`):
lê `SpiStatus` por `(RefSource, TxSts)`; se não encontra a chave, devolve
`enumStatusOperacao.PendenteVerificacaoCredito` (default seguro NÃO terminal).

`TrataStatusCode(tags, res)` (`SPI.Core.Mensageria.Geral.Application.decompiled.cs:2554-2575`):
lê o TxSts do XML (`TagSts`), e chama `InterpretaConteudo(RefSourceStatusMsg, MsgStatusCode)`.
Exceção codificada só para PAIN.014 (`res.MsgStatusCode == "ACSP" ? Efetivada : Rejeitada`).
O `RefSourceStatus`/`TagSts` por tipo de mensagem vem do catálogo `SpiCadMessage`.

### 1.3 Correlação da pacs.002 com a operação original

`SPI.Core.Worker.TratamentoRetornoApiBacen.Infrastructure.decompiled.cs:949-963`: para uma
`PACS.002`, correlaciona por `ObtemMensagemOriginalPorEndToEndId(retorno.EndToEndIds)` (=
`OrgnlEndToEndId`). Se o número de mensagens impactadas for menor que a quantidade de E2E
distintos, marca `enumValidationError.NaoEncontrado` "Operação original não encontrada na
base de dados. **Enviando operação para reprocessamento**" (linha 961/616) — ou seja, pacs.002
sem par NÃO é descartado, vai para REPROCESSAMENTO (retry, at-least-once).

### 1.4 O guardião terminal-safe: proc SPI_SPUPDSTATUSMESSAGE

Chamado via `SPI_SPUPDSTATUSMESSAGE2` (cursor por lote) no worker filastatus:
`SPI.Core.Worker.FilaEntradaStatus.Infrastructure.decompiled.cs:1691`.

Proc `DBO.SPI_SPUPDSTATUSMESSAGE`
(`LegadoPIX/Pix/SPI/Database-Inicial/Base CRK_SPI/Procedures/DBO.SPI_SPUPDSTATUSMESSAGE.sql:65-99`):

```sql
UPDATE MSG
SET MSG.IdStatus = CASE
    WHEN @AtualizaStatusOperacao = 'S' THEN
        CASE
            WHEN CLASSIF.EhStatusFinal = 'S' THEN MSG.IdStatus  -- JÁ terminal: MANTÉM
            ELSE @IdStatus                                       -- não terminal: aplica
        END
    ELSE MSG.IdStatus END,
    MSG.ResourceId = COALESCE(MSG.ResourceId, @ResourceId),               -- first-write-wins
    MSG.DtHrUltimaAlteracao = CASE WHEN ... < @DtHrUltimaAlteracao THEN @DtHrUltimaAlteracao ...END, -- monotônico
    MSG.DtHrMsg/DtHrTermino/DtContabil/DtHrLiquidacao = COALESCE(...)      -- first-write-wins
FROM SpiMessage MSG WITH (UPDLOCK)                                        -- lock de escrita
INNER JOIN SpiStatusOperacao STATUS ON MSG.IdStatus = STATUS.IdStatus
INNER JOIN SpiClassifResultado CLASSIF ON CLASSIF.Resultado = STATUS.Resultado
WHERE MSG.IdOperacao = @IdOperacao
```

O `CLASSIF` é o join sobre o status ATUAL da linha. Se o status atual já é final
(`EhStatusFinal='S'`), o novo status NÃO é aplicado. **Uma vez Efetivada/Rejeitada, nenhum
pacs.002 tardio, camt fora de ordem ou reentrega reverte** (CAS por construção, sob UPDLOCK).
`SpiMessageIds` (linha 106-110) registra cada MessageId por operação (ledger de dedup).

### 1.5 Crédito ao core legado SÓ no status final

`MontaAlerta` (`SPI.Core.Worker.FilaEntradaStatus.Infrastructure.decompiled.cs:536-550`):
retorna null se `!idStatus.EhStatusFinal()` (linha 538); o tipo de alerta é
`MensagemEfetivada` se `EhTerminoOK()` senão `MensagemRejeitada` (linha 550). A confirmação de
CRÉDITO ao legado só é montada quando `EhStatusFinal() && DebitoCredito == Credito`
(`...:1115`). Logo o legado confirma crédito/débito ao core no status TERMINAL (Efetivada),
que é atingido por **pacs.002 ACCC/ACSC OU camt.054 BOOK, o que chegar primeiro** (o proc
dedup por terminal-safe garante uma vez só).

### 1.6 camt.054 BOOK e admi.002 (fallback) no legado

`ImportaCAMT0054` (`TratamentoRetornoApiBacen.Infrastructure.decompiled.cs:1068-1101`):
`Sts/Cd == "BOOK"` → Efetivada; `AddtlNtryInf` presente → Rejeitada; casa pacs.008 por
`Refs/EndToEndId` e pacs.004 por `Refs/InstrId`. `AjustaStatusCAMT054` (linha 2060-2071)
só flipa se ainda NÃO estava Efetivada/Rejeitada (idempotente). Observação: esse caminho
grava via EF direto (não pelo proc), mas é idempotente por checagem explícita.

`RejeitaPgtoPorADMI002` (linha 2079-2100): fecha a consulta camt.060 presa. Correlação por
`IdEventoPesquisa`: prefixo `E` → PACS.008 por EndToEndId; senão → PACS.004 por RtrId. Rejeita
só se NÃO já Efetivada/Rejeitada/Pendente (terminal-safe).

### 1.7 camt.060 aos 50 minutos (reconcilia preso) no legado

`PagamentosPendentes(ISPB)` (`SPI.Core.Worker.EnvioConsultaOperacao.Infrastructure.decompiled.cs:116-123`):
seleciona PACS.008/PACS.004 com `IdStatus IN (0,1,2,4,5,7,13)` (todos NÃO terminais no
numeração legado), `DtHrOperacao < now-50min`, `TipoPrioridade == "PAGPRI"` (money-path).
50 minutos sem desfecho → dispara camt.060; a resposta (camt.052/054 ou admi.002) fecha.

### 1.8 pacs.028 no legado: NÃO EXISTE

Catálogo `CRK_SPIDOMINIO.dbo.SpiCadMessage` (DB vivo) só tem PACS.002, PACS.004, PACS.008:

| CdMsg | FlFinanceira | CdMsgResposta | CdMsgErro | Classificacao |
|---|---|---|---|---|
| PACS.002 | 1 | NULL | ADMI.002 | 2 |
| PACS.004 | 1 | PACS.002 | ADMI.002 | 1 |
| PACS.008 | 1 | PACS.002 | ADMI.002 | 1 |

Não há linha `PACS.028` no catálogo, no decompilado (`grep pacs.028/PmtStsReq` = 0 hits) nem
nos scripts SQL (`LegadoPIX/Pix` = 0 hits). A consulta de status de operação do legado é a
**camt.060** (§1.7), não pacs.028. VERDICT: pacs.028 é inexistente no legado por desenho.

---

## 2. NOSSO — como o pacs.002 vira status (com prova)

### 2.1 Mapa de status (código, não tabela)

`apps/spi_service/lib/spi_service/workers/inbound_processor.ex:2568-2574`
(`map_pacs002_status/1`):

```elixir
def map_pacs002_status("ACSP"), do: "processing"
def map_pacs002_status("ACCC"), do: "settled"
def map_pacs002_status("ACSC"), do: "settled"
def map_pacs002_status("STLD"), do: "settled"
def map_pacs002_status("RJCT"), do: "rejected"
def map_pacs002_status("CANC"), do: "cancelled"
def map_pacs002_status(_), do: nil   # desconhecido: log + sem aplicar
```

Catálogo de status (`apps/shared/lib/shared/spi/status_codes.ex:36-104`):
2 ACSP (final:false), 3 ACCC (final:false), **4 ACSC (final:true, `settled_id`)**,
5 ACTC (false), 6 ACWC (false), 7 STLD (final:false), **8 RJCT (final:true)**,
9 CANC (final:true), 10 RTRN (final:true). `@terminal_ids = {4,8,9,10}` (linha 112).

### 2.2 Roteamento do status string → handler (StatusUpdater)

`apps/spi_service/lib/spi_service/workers/status_updater.ex:87-96`:
`"processing" → handle_processing` (grava ACSP, status 2, NÃO terminal, sem mexer em saldo,
linha 134-157); `"settled" → handle_settlement → settle_payment_line` (grava ACSC=4, terminal,
e publica o evento settled ao Core, linha 267-332); `"rejected" → handle_rejection`
(grava RJCT=8, terminal, linha 468-478); `"accepted_settlement" → handle_accepted_settlement`
(grava ACCC=3 — string NÃO produzida por map_pacs002_status, então inerte para pacs.002).

Correlação da pacs.002 com a operação original por `//OrgnlEndToEndId`
(`inbound_processor.ex:2478-2490`), com detecção de RtrId (prefixo `D`) para casar a linha da
DEVOLUÇÃO em vez do pagamento original (2484-2490). Lote parcial suportado
(`process_multi_status`, 2360-2464): desfecho item a item por E2E próprio.

### 2.3 Terminal-safe (CAS + guards)

- `terminal_status?/1` (status_updater.ex:955) sobre `@terminal_statuses`.
- `guard_not_terminal/2` (959-970): ignora pacs.002 fora de ordem se já terminal.
- `guard_not_settled/1` (974-976): settlement idempotente (no-op se já ACSC=4).
- `transition_status/4` (984-1002): **CAS real** — `UPDATE ... WHERE id = ^tx.id AND
  status_id IN ^allowed_from`; conta linhas afetadas; só a entrega que efetivou a transição
  executa efeitos de dinheiro (usado nas linhas de devolução, `@return_line_outcome_allowed_from`
  = PDNG/ACSP/ACCC/ACTC/ACWC).
- `publish_status_update_for_tx/4` (inbound_processor.ex:4160-4232): guarda
  `if terminal_status?(tx.status_id) → :ok` (não republica), publica o evento atômico
  `monetarie.spi.transaction.status_update` via outbox (fail-CLOSED: falha = NAK).
- `settle_payment_line` (status_updater.ex:267-332): `guard_not_settled` + `guard_not_terminal`
  + update ACSC(4) + `confirm_balance_block` + publica `monetarie.spi.transaction.settled` numa
  `Repo.transaction` (outbox). `:already_settled`/`:terminal` = no-op idempotente.

### 2.4 Autoridade de liquidação e ordem (pacs.002 → camt.060 → camt.054), sem duplo-crédito

- pacs.002 ACCC/ACSC/STLD → "settled" → settle_payment_line → ACSC(4) terminal + evento
  settled ao Core (netting/crédito). É o gatilho PRIMÁRIO de crédito
  (`map_pacs002_status:2569-2571`; comentário de desenho `inbound_processor.ex:2557-2567`).
- camt.060 reconcilia operação PRESA: `PaymentStatusReconciler`
  (`payment_status_reconciler.ex:26-32`) `@stuck_after_seconds 90`, tick 30s,
  `@pending_status_ids [1,2]` (PDNG/ACSP), envia camt.060 com `ReqdMsgNmId: "camt.054"`
  (`send_operation_query`, 150-161).
- camt.054 BOOK é FALLBACK: `classify_camt054_entry` (`inbound_processor.ex:1591-1619`)
  `Sts BOOK + DBIT → {:ok,"settled",...}`; `resolve_outbound_from_camt054` (1547-1564) resolve
  a pacs.008 OUTBOUND por E2E e chama `publish_status_update_for_tx(tx, e2e, "settled", ...)`.
  Como esse caminho passa pelo MESMO guard terminal (2.3), **se a pacs.002 já liquidou (ACSC=4
  terminal), o camt.054 vira no-op** (não há segundo evento settled). O inverso (camt.054
  primeiro, pacs.002 depois) também: a pacs.002 encontra a linha terminal e é ignorada.
  **Não há duplo-crédito na ordem primária→fallback.**
- Fecho por admi.002: `maybe_correlate_admi002_camt060` (1858-1888) fecha a camt.060 como
  not_found; `maybe_publish_admi002_rejection` (1931-1964) publica "rejected" na pacs.008
  correlacionada (guardado por terminal — não rejeita liquidada).

### 2.5 pacs.028 no nosso código: REMOVIDA como fictícia

Limpeza sistemática "V2-01b" (pacs.028 não está nos XSD raw v5.12 SPI):
- `inbound_processor.ex:2576` "process_status_request (pacs.028) deletado — fictícia".
- `outbound_sender.ex:10`; `shared/bacen/spi_client.ex:413`
  ("send_additional_payment_info (pacs.028) deletado"); `validation/message_schemas.ex:143`;
  `shared/bacen/simulator/response_generator.ex:406` ("respond_to_pacs028 deletado");
  `settlement_service_web/message_form_catalog.ex:111`;
  `controllers/message_controller.ex:1105`; seeds `gap_resolution_seed.sql:348,386,467`.

Consulta de status = camt.060 (§2.4). VERDICT: pacs.028 inexistente por desenho, igual ao
legado.

### 2.6 Frontend (Vue) — labels de status

`pix/frontend/admin/src/utils/statusLabel.ts:14-28` cobre `settled→Liquidado`,
`rejected→Rejeitada`, `cancelled→Cancelada`, `returned→Devolvida`, `processing→Em
Processamento`, `accepted_settlement→Aguardando liquidação`. Correta a exibição da máquina de
status. Resíduo cosmético: `useIso20022Labels.ts:24` ainda tem
`'pacs.028': 'Investigação de Status (pacs.028)'` embora o backend tenha removido a pacs.028.

---

## 3. GAPS (legado × nosso)

### G1 [coberto] Mapa de status do pacs.002 (ACSP não terminal, ACCC/ACSC liquida, RJCT rejeita)

- Legado: de-para `SpiStatus` RefSource 6 → ACCC/ACSC=9 Efetivada, ACSP=11
  PendenteVerificacaoCredito (não terminal), RJCT=10 Rejeitada
  (`CRK_SPIDOMINIO.dbo.SpiStatus`; `InterpretaConteudo` `Mensageria.Geral.Application:3020-3049`).
- Nosso: ACSP→processing (status 2, não terminal), ACCC/ACSC→settled (ACSC 4, terminal),
  RJCT→rejected (8, terminal) (`inbound_processor.ex:2568-2574`; `status_updater.ex:87-96,134,267`).
- Semântica idêntica nos 4 códigos compartilhados. STLD→settled é adição nossa (não conflita).

### G2 [coberto] Máquina de status terminal-safe (idempotente, sem regressão)

- Legado: `SPI_SPUPDSTATUSMESSAGE` mantém status se `CLASSIF.EhStatusFinal='S'` sob UPDLOCK;
  COALESCE em datas/ResourceId; SpiMessageIds ledger
  (`DBO.SPI_SPUPDSTATUSMESSAGE.sql:65-110`).
- Nosso: `guard_not_terminal` + `guard_not_settled` + `transition_status` CAS
  (`status_updater.ex:955-1002`) + guard terminal em `publish_status_update_for_tx`
  (`inbound_processor.ex:4164-4170`). pacs.002 fora de ordem/reentrega = no-op.

### G3 [coberto] Ordem de autoridade: pacs.002 credita, camt.060 reconcilia, camt.054 é fallback, sem duplo-crédito

- Legado: crédito ao core só no status FINAL Efetivada, atingido por pacs.002 ACCC/ACSC ou
  camt.054 BOOK primeiro-vence (`MontaAlerta` null se `!EhStatusFinal`, `FilaEntradaStatus.Infrastructure:538,1115`;
  `ImportaCAMT0054:1074-1101`; camt.060 50min `EnvioConsultaOperacao.Infrastructure:123`).
- Nosso: pacs.002 settled é o gatilho primário; camt.060 (90s) reconcilia PDNG/ACSP; camt.054
  BOOK resolve por E2E através do mesmo guard terminal → 2º gatilho vira no-op
  (`payment_status_reconciler.ex:27-32`; `inbound_processor.ex:1547-1564,4164-4170`;
  `status_updater.ex:267-332`). Ordem correta e sem duplo-crédito, conforme o desenho pedido.
- Correção ao laudo 11 (D1): o legado NÃO credita "só no camt.054 BOOK"; credita no primeiro
  terminal Efetivada, que inclui pacs.002 ACCC/ACSC. Portanto nosso comportamento está ALINHADO.

### G4 [coberto] pacs.028 (consulta de status)

- Legado: inexistente (catálogo `SpiCadMessage` só PACS.002/004/008; 0 hits em decompilado e
  SQL). Status query = camt.060.
- Nosso: removida como fictícia em todo o stack (`inbound_processor.ex:2576`,
  `spi_client.ex:413`, `message_schemas.ex:143`, etc.). Status query = camt.060.
- Paridade por ausência intencional dos dois lados.

### G5 [divergencia, risco baixo] pacs.002 SINGLE sem operação correlata: nós damos ACK-drop; o legado REPROCESSA

- Legado: pacs.002 cujo OrgnlEndToEndId não casa nenhuma operação → `NaoEncontrado` "Enviando
  operação para reprocessamento" (retry/at-least-once)
  (`TratamentoRetornoApiBacen.Infrastructure:616,961`).
- Nosso: no caminho SINGLE, `publish_status_update` sem tx correlata faz `Logger.error` +
  `:ok` (ACK, sem retry) (`inbound_processor.ex:4126-4137`). No caminho MULTI-item o desfecho
  é `{:error,:uncorrelated}` → NAK/reprocessa (`apply_status_item:2436`).
- Assimetria: single ACK-dropa, multi reprocessa. Mitigação real: a nossa saída é persistida
  ANTES do POST, então o pacs.002 normal encontra a linha; e o `PaymentStatusReconciler`
  (camt.060 aos 90s) recupera saída presa. Risco de perda prática = baixo.

### G6 [divergencia, risco info] Limiar e escopo do camt.060 de reconciliação

- Legado: 50 minutos, conjunto amplo `IdStatus IN (0,1,2,4,5,7,13)` incluindo estágios
  pré-envio (`EnvioConsultaOperacao.Infrastructure:123`).
- Nosso: 90 segundos, conjunto estreito `[1,2]` (PDNG/ACSP = já enviado, aguardando pacs.002)
  (`payment_status_reconciler.ex:27-31`). Estágios pré-envio são tratados por
  OutboundSender/StuckOutboundChecker, não pelo reconciliador. Nós reconciliamos MUITO mais
  cedo e mais estreito por desenho; não é defeito, é diferença de timing.

### G7 [divergencia, risco info] De-para dirigido por tabela (config) vs mapa hardcoded (código)

- Legado: `SpiStatus` é tabela editável; operador adiciona/muda um TxSts→status sem deploy;
  default seguro = PendenteVerificacaoCredito (`InterpretaConteudo:3048`).
- Nosso: `map_pacs002_status/1` é código; um TxSts novo do BACEN exige deploy; desconhecido →
  nil (log + sem aplicar) (`inbound_processor.ex:2574`). Comportamento default equivalente
  (não aplica sem certeza), mas sem flexibilidade de configuração em runtime.

### G8 [info] Quirk interno: ACCC(3)/STLD(7) marcados `final:false` no catálogo de status

- `status_codes.ex:41-46,72-78` marca ACCC(3) e STLD(7) como `final:false`, mas o pacs.002
  ACCC/ACSC/STLD SEMPRE persiste como ACSC(4) terminal via settle_payment_line, e a linha da
  devolução BOOK grava STLD(7) com guarda local explícita `or ret_tx.status_id == 7`
  (`inbound_processor.ex:1646`). Sem impacto funcional; a coerência do estado terminal é
  garantida caso a caso.

### G9 [info] Resíduo cosmético de pacs.028 no frontend

- `pix/frontend/admin/src/composables/useIso20022Labels.ts:24` mantém o label
  `'pacs.028': 'Investigação de Status (pacs.028)'` embora o backend a tenha removido. Só
  exibição; não há trilho pacs.028 vivo.

---

## 4. Resumo dos vereditos

| ID | Título | Tipo | Risco |
|----|--------|------|-------|
| G1 | Mapa pacs.002 (ACSP não terminal, ACCC/ACSC liquida, RJCT rejeita) | coberto | info |
| G2 | Máquina de status terminal-safe | coberto | info |
| G3 | Ordem pacs.002→camt.060→camt.054 sem duplo-crédito | coberto | info |
| G4 | pacs.028 (consulta de status) inexistente dos dois lados | coberto | info |
| G5 | pacs.002 single sem correlação: ACK-drop vs reprocessamento | divergencia | baixo |
| G6 | Limiar camt.060 90s/estreito vs 50min/amplo | divergencia | info |
| G7 | De-para por tabela (config) vs mapa hardcoded (código) | divergencia | info |
| G8 | ACCC(3)/STLD(7) final:false no catálogo | parcial | info |
| G9 | Resíduo de label pacs.028 no frontend | divergencia | info |

Conclusão: a máquina de status do pacs.002 e a ausência do pacs.028 estão em PARIDADE
semântica com o legado, e a ordem de autoridade de liquidação pedida (pacs.002 credita,
camt.060 reconcilia preso, camt.054 BOOK fallback) está implementada SEM duplo-crédito. As
divergências reais são de granularidade operacional (G5 ACK-drop no single, G6 timing do
camt.060, G7 config vs código), todas de risco baixo/info, com rede de segurança (camt.060)
cobrindo o caso de perda.
