# PRD: TED manual STR0008 de R$ 10,00 sem débito no extrato (teste da Maiza, 17/07/2026 14:07 BRT)

Investigação read-only em produção, sem nenhuma alteração de dado. Todas as consultas via ECS exec rpc (SELECT) nos bancos vivos do core-api e spb-api, logs CloudWatch e XMLs do acervo.

## O que aconteceu (resumo)

1. Às 14:07:25 BRT um operador fez um envio MANUAL de STR0008 pela tela do spb-admin (`bacen_messages.source = "operator_send"`, audit `message_send` às 17:07:25.588Z, IP 10.50.1.133, `user_id` nulo na auditoria). Valor R$ 10,00, histórico "teste".
2. O lastro do envio manual FUNCIONOU: a cabine chamou o `CoreDebitValidation.register` (NATS `core.validate_debit.request`), o Core validou saldo, criou o hold `49382c83d4d1e81aa42c5e160c719f01` e a transação pendente `MON20260717764179292` (conta 2630, metadata `source=spb_manual_register`) às 17:07:25.412Z.
3. O BACEN liquidou: STR0008R1 com `SitLancSTR=1` às 14:07:28, `NumCtrlSTR STR20260717033692271`. Reserva debitada R$ 10,00 (`balance_movements` grupo 15, confirmed 17:07:35.343Z).
4. **CAUSA RAIZ DO SINTOMA**: no `r1_confirmed`, o `LifecycleEngine.notify_core_if_applicable` leu `bacen_messages.id` por `Repo.query` cru (uuid vem como 16 bytes binários), o `CoreNotifier.notify_status_change` põe esse binário em `"message_id"` e o encode JSON estourou: log de PROD `17:07:35.580 [error] [CoreNotifier] Exception: invalid byte 0xD2 in <<210, 108, 75, 23, ...>>` (os bytes são exatamente o uuid `d26c4b17-6e8c-483b-b07a-426247dd84f2` da mensagem). O `rescue` engoliu a exceção e o evento `monetarie.spb.transactions.confirmed` NUNCA foi publicado. Log do core-api: zero ocorrências de `MON20260717764179292` na janela.
5. A reentrega do R1 às 17:07:37 foi descartada como "already terminal" (idempotência), sem repetir o notify. Sem o evento `confirmed`, o Core não completa a transação e não grava a linha de débito no extrato (o débito de TED enviada só nasce em `StatementEntries.record_outbound_settled`, disparado pelo evento confirmed/settled; fix de 15/07, deployado).
6. Às 14:09 o Bradesco devolveu os R$ 10,00 (STR0006, mesma conta de origem). O trilho INBOUND funcionou 100%: `spb_inbound_credits` status `credited_member`, depósito TB idempotente, linha "TED recebida" no extrato (`account_entries` +100000 subcent, ref `STR20260717033713740`), transação `SPBCRSTR...` completed, `cosif_je created`.
7. **Desfecho capturado AO VIVO às 17:41:01Z**: o `StaleHoldChecker` (30 min, cron 1-59/5) marcou a transação como `timeout` ("No response within 30 minutes") e DEVOLVEU o hold à conta (`HoldRelease ... amount=100000`). Ou seja, o débito da TED enviada nunca será efetivado por esse trilho, e o desfecho automático foi o ERRADO para uma TED que o BACEN JÁ tinha liquidado.

## Respostas às validações pedidas

**Origem da STR0006 vs destino da STR0008**: CONFEREM byte a byte nos XMLs. STR0008 creditou ISPB 60746948 (Bradesco), Ag 2276, CC 351725, CNPJ 46026562000105, "Monetarie Soc de Credito Direto". A STR0006R2 recebida tem como debitado exatamente ISPB 60746948, Ag 2276, CC 351725, CNPJ 46026562000105, "MONETARIE SOCIEDADE DE CREDITO D. S.A". É ida e volta na mesma dupla de contas (finalidade 110 na volta).

**Conta debitada no envio da STR0008**: no XML, Ag 0001 / CC 526000050 na Monetarie. Isso resolveu no Core para a conta id 2630 (número 052600005, dígito 0, agência 0001), titular user 2583 = MONETARIE SOCIEDADE DE CREDITO DIRETO S.A, CNPJ 46.026.562/0001-05 (a própria instituição como cliente). O débito ficou APENAS como hold two-phase e foi DEVOLVIDO às 17:41Z pelo StaleHoldChecker; o débito definitivo nunca aconteceu. Na reserva BACEN o débito de R$ 10,00 aconteceu e está registrado (balance_movements).

**Conta creditada no recebimento da STR0006**: a MESMA conta 2630 (XML: Ag 1 / Ct 526000050), via ponte `spb_inbound_credits` com resolução `member_account`, crédito TB + extrato + transação completed.

## Estado do dinheiro agora (verificado)

- Reserva BACEN: débito 10,00 (STR0008) + crédito 10,00 (STR0006) = zero no round-trip. Cabine reconciliada.
- Conta 2630: TB = 7.697.427.100 subcent = R$ 769.742,71 = soma de `account_entries` = tela do extrato. TB == PG.
- PORÉM a conta ficou com **+R$ 10,00 SEM lastro**: o crédito da volta entrou, o débito da ida nunca foi efetivado (hold devolvido). Como a conta é da própria Monetarie, não há dano a terceiro, mas há drift de ledger de R$ 10,00 (passivo de cliente criado sem contrapartida). Reparo (decisão do dono): efetivar o débito de R$ 10,00 na conta 2630 + gravar a linha "TED enviada" de 17/07 + marcar a transação como settled com as referências BACEN (NumCtrlSTR STR20260717033692271); conferir também se o espelho COSIF da perna de saída existe.

## Defeitos encontrados (por severidade)

1. **P0 estrutural, vale para TODA TED de saída futura (IB, partner, manual com lastro)**: `CoreNotifier.notify_status_change` + `notify_core_if_applicable` (spb `lifecycle_engine.ex:2338-2361`, `core_notifier.ex:24-63`) quebram quando os 16 bytes do uuid da mensagem não são UTF-8 válido (a maioria dos uuids). O notify morre em silêncio (rescue só loga), o Core nunca fica sabendo do desfecho, a transação apodrece em processing. Fix: carregar o id com `Ecto.UUID.load` antes de montar o payload, e alertar (não só logar) quando o notify falhar. A MON20260717764179292 é a ÚNICA TED outbound do Core desde 01/07, então não há acervo represado, mas segunda-feira 21/07 o cliente roda sequências e TED do IB cai exatamente nesse trilho.
2. **P0 de desfecho**: o `StaleHoldChecker` aplica timeout CEGO + devolução de hold em TED outbound sem consultar a cabine (a mesma classe que o item 5 fechou para devoluções PIX e o R6 fechou para PIX-out). Aqui ele devolveu um hold de dinheiro que o BACEN JÁ tinha movido. TED presa em processing precisa consultar o estado da operação na cabine (r1_confirmed => efetivar débito, não liberar).
3. **P1 auditoria**: `audit_logs` da cabine registrou o `message_send` com `user_id` nulo. Não dá para provar QUEM fez o envio manual pela trilha de auditoria (só IP interno 10.50.1.133 e o contexto do WhatsApp).
4. Observação de reentrega: o replay do R1 (17:07:37) foi tratado como idempotente sem repetir o notify perdido. Correto não duplicar, mas o notify deveria ser durável (outbox) para sobreviver à falha do primeiro processamento.

## REPARO EXECUTADO EM PRD (17/07 ~15:00 BRT, ordem do dono)

Débito de R$ 10,00 EFETIVADO na conta 2630 replicando o fluxo normal de liquidação:
- TB transfer cliente→pool de liquidação, 100000 subcent, novo hold `8579386629d89b1153a89e6433719f01` (o hold original `49382c83...` tinha sido devolvido pelo watchdog; `released_hold_id`/`hold_released_at` preservados no metadata).
- Transação `MON20260717764179292`: `payment_status=settled`, `bacen_reference=STR20260717033692271` (`num_ctrl_str`), `error_reason` limpo, metadata com trilha completa do reparo.
- Linha de extrato gravada via `StatementEntries.record_outbound_settled`: `-100000` subcent, "TED enviada - Monetarie Soc de Credito Direto", ref `MON20260717764179292`, entry_date 17/07.
- VERIFICADO: TB = soma account_entries = **7.697.327.100 subcent = R$ 769.732,71** (round-trip zerado). Extrato com as DUAS pernas.
- Incidente do reparo (registrado): o primeiro UPDATE de metadata usou `metadata || $4::jsonb` com STRING JSON (virou array e quebrou o load Ecto); corrigido no passo seguinte reescrevendo o objeto completo. A perna TB executou UMA vez só (pré-condições + verificação de saldo antes/depois).

## FIXES DE CÓDIGO (TDD, RED→GREEN, commits `7c990cd1` spb + `cbd21c1b` core, pushados)

1. `CoreNotifier.id_string/1` (spb): normaliza o uuid cru de 16 bytes na fronteira do payload (`Ecto.UUID.load`); testes reproduzindo o byte 0xD2 do incidente + fallback de transaction_id. Suítes: core_notifier 8/0, nats 10/0, consumers 47/0.
2. `StaleHoldChecker.quarantine_stale_ted/1` (core): TED outbound NUNCA sofre timeout cego; quarentena barulhenta (hold preservado + telemetria `[:monetarie, :ted, :hold_stale]`); desfecho pertence ao `SpbStatusReconciliation`. Teste novo + PIX preservado; workers 201/0.
3. CONFIG PROD (I1): a rede de segurança `SpbStatusReconciliation` estava INERTE em PROD — task-def sem `SPB_CABIN_API_BASE_URL`/`SPB_CABIN_ADMIN_LOGIN`/`SPB_CABIN_ADMIN_PASSWORD` (provado no log: "SPB cabin API not configured" às 17:36Z varrendo exatamente a TED do incidente). Adicionados no deploy: base URL `http://spbapi.monetarie.internal`, login de PROD, secret `monetarie/prod/admin/spb/initial_password`.

## Evidências

- XMLs STR0008 / STR0008R1 / STR0006R2 no acervo `bacen_messages` (control MON20260717764179292 e STR20260717033713740).
- `spb_operations` 706f2081 (outbound r1_confirmed) e 3814fea9 (inbound r2_confirmed).
- `balance_movements` grupo 15: debit predicted+confirmed (17:07) e credit confirmed (17:09).
- Core `transactions`: MON20260717764179292 (timeout, hold devolvido 17:41:01Z) e SPBCRSTR20260717033713740 (completed).
- `account_entries` conta 2630: só o crédito de 17/07 (+100000 subcent) e o PIX de 16/07; sum = 7.697.427.100 = TB.
- Logs CloudWatch spb-api 17:07:35.580 (exceção CoreNotifier) e core-api 17:41:01 (StaleHoldChecker timeout + release).
