# Design: fim do "Aguardando retorno" + verdade no Monitor + Movimento diário SPB — 2026-07-14

## Princípio (ordem do dono)

O BACEN nunca deixa de responder uma mensagem. "Aguardando retorno" não é um
estado final aceitável: se a pacs.002 se perdeu, o sistema tem a obrigação de
consultar a operação via camt.060 e atualizar o status sozinho. A tela nunca
pode mentir: o status exibido vem da fonte de verdade (`messages.status_id`),
não de uma derivação por mensagens presentes.

## Trilha 1 — Auto-resolução de pacs.008 sem resposta (money path)

1. **`SpiService.Workers.PaymentStatusReconciler`** (novo, tick 30s):
   seleciona pacs.008 OUTBOUND em `monetarie_spi.messages` com
   `status_id IN (1,2)` (PDNG/ACSP) e `operation_time < now() - 90s`
   (limite 10 por tick). Para cada uma, dispara camt.060 **consulta de
   operação** (`event_id` = E2E, `ReqdMsgNmId=camt.054`, sem `RptgPrd` —
   formato provado vivo em 14/07). Backoff: não reconsulta o mesmo E2E se
   existe `camt060_requests` com `action = "op_status:"<>e2e` e `sent_at`
   nos últimos 5 minutos. Registro via `Camt060Requests.record_sent/3`
   (correlação camt.054 → request já existente).
2. **Resolução no `InboundProcessor.process_notification` (camt.054)**: se a
   notificação carrega `NtryDtls/Refs/EndToEndId` de uma transação OUTBOUND
   nossa em estado não-terminal:
   - `Sts=INFO` + código de rejeição em `AddtlNtryInf` → publica o MESMO
     evento da pacs.002 RJCT (`monetarie.spi.transaction.status_update`,
     `new_status="rejected"`, `reason_code` = código, response_message_type
     "camt.054") — o StatusUpdater aplica status 8 + histórico + evento
     `transaction.rejected` para o Core (void da reserva) + saldo PI.
   - `Sts=BOOK` com lançamento DBIT → `new_status="settled"` (liquidada).
   - Nenhum caminho novo de dinheiro: reuso integral da máquina existente.

## Trilha 2 — Monitor de Operações fala a verdade

O status de negócio do fluxo era DERIVADO das pernas presentes (pacs.008 sem
pacs.002 = "Aguardando retorno" para sempre — mentira quando a transação já
foi resolvida por outra via). O backend (`monitor_controller`) passa a anexar
a cada fluxo o **status atual da transação** (`messages.status_id` por E2E,
código ISO); o frontend prioriza esse campo sobre a derivação por pernas.
"Aguardando retorno" só aparece quando a transação está de fato pendente — e
com a Trilha 1, esse estado se resolve sozinho em ~2 minutos.

## Trilha 3 — Movimento diário SPB com dados completos

- **Entidade externa** = nome da instituição CONTRAPARTE (outbound →
  `receiver_ispb`, inbound → `sender_ispb`, resolvido em `institutions`).
  "Banco Central do Brasil" só quando a contraparte é o BACEN de fato (ex.:
  LPI0002 reserva↔Conta PI). Nunca o nome do SISTEMA ("STR").
- **Finalidade, CPF/CNPJ débito/crédito, agência/conta**: o CSV já extrai
  esses campos do `xml_content`; o preview da tela passa a expor as MESMAS
  colunas (uma única projeção para tela e arquivo).

## Validação

Suites completas verdes; validação viva em HML e PROD nas TELAS (screenshots
das linhas reais), não só nos endpoints. Caso kot2yx/ryxvl2u4w6g como prova:
nenhuma linha "Aguardando retorno" com transação já resolvida.
