# Handoff 2026-07-14 — Ciclo automático SISBAJUD×STA + 2 merges da main + paridade total

> Retomar via `/retomar`. Fecha a frente **SISBAJUD×STA** (análise completa + implementação do ciclo automático de ponta a ponta) e mantém a branch em **paridade total com a main** (2 merges na sessão). Tudo pushado COM autorização explícita do dono (`c2da1878..059b045a`).

## Estado da branch (canônico)

- `origin/feat/monetarie_improviments` = **`c0fb3def`** (pushado, branch 0/0 com o remoto, 0 atrás da main).
- Commits da sessão:
  - `a9683298` — Merge da main (5 commits): paridade extrato/saldo LegadoPIX + janela surda ICOM.
  - `74a4290a` — Merge da main (22 commits): money-path SPB + gate XSD oficial v5.12 + DICT/envio manual PIX + relatórios operador.
  - `aeaee2c0` — **Ciclo automático SISBAJUD×STA**: conteúdo no evento + JUD no poller + auto-send opcional do 5302.
  - `65330019` — Ingestão via STA vira job Oban durável (achados ALTA do review adversarial).
  - `059b045a` — Consumo STA durável (dono único) + watchdog do 5302 preso em `delivering`.
  - `6e46a518` — CAS na demoção do watchdog + guarda de estado terminal nos eventos STA.
  - `c0fb3def` — `uploaded` tardio sobre estado terminal absorve os dados sem tocar no status.
- Ambiente local no ar e validado: core-api (:4002, **com `NATS_STREAM_REPLICAS=1` — ver gotcha novo**), pix-api (:4003/14001/14002), spb-api (:4010), core-admin (:5180) — todos rebuildados nesta sessão com o código pós-merge.

## DECISÃO do dono (registrar)

**Envio do 5302: manual continua sendo o DEFAULT (o operador valida na tela antes de enviar), e o envio 100% automatizado existe como OPÇÃO.** Os dois modos convivem, governados pela flag `SISBAJUD_AUTO_SEND_ENABLED` (`:monetarie, :sisbajud_auto_send`, default `false` no `runtime.exs`). ON = a própria ingestão dispara o mesmo `Delivery.send_via_sta` do botão (mesmos guards anti-duplicidade, fail-soft: falha de envio nunca desfaz nem atrasa o processamento das ordens).

## 1. Merges da main (paridade total)

- **`a9683298` (5 commits, todos pix/cabine):** "saldo atual" sem `RptgPrd` (posição corrente de verdade), **remuneração da Conta PI creditada** (era receita jogada fora, com dedup por message_id), `AddtlRptInf` é nome de arquivo (não digest — gate destravado), tela camt.060 com retorno real + histórico, e o P0 da **janela surda do ICOM no deploy** (readiness probe + 410 Gone sem backoff + sessão `error` reaproveitada + `open` > `error`). Merge limpo (zero interseção de arquivos). Validação: pix spi_service 742/0, shared 3/3, core Fase 1+2 18/18, vue-tsc limpo nos arquivos do merge.
- **`74a4290a` (22 commits):** money-path SPB (TED sem valor barrada, 5 buracos, quarentena consertada, R1 uma linha), **gate de envio SPB validando contra o XSD OFICIAL v5.12** (catálogo versionado em `spb/services/bacen_gateway/priv/xsd/v512/`, ~400k linhas), DICT 503 + envio manual PIX validando conta/saldo no Core (responder novo `pix_debit_validator`), rejeições PIX com motivo certo, redelivery de mensagem não-entregue, Relatório de Recebimentos (Core) e relatórios do operador (SPB), Liquidação 10.000x + Identificador BACEN por trilho. **Conflitos (4):** `SettlementBalanceView.vue` = THEIRS (contrato `*Subcent` — unidade no nome do campo, backend+tela consistentes; nada nosso se perdeu); 3 locales SPB = COMBINE. **Auto-merges verificados semanticamente (5)** — incl. constatação de que a remoção da rota LoanAccrual é nossa e intencional (`f6bcb7b9`). **Prova de zero regressão no SPB:** suíte completa (17.627 testes) rodada na branch E na main pura em ambiente idêntico → diff de falhas VAZIO (82 baseline, machine-dependent: `md/` e `api_gateway` não versionados; a main falha em 4 que nós passamos). Migration nova única `20260714150000` aplicada no `mon_core` local. Core 649/0, vitest admin 330/330, spb frontend vue-tsc 0 erros + vitest 38/38.

## 2. Análise completa do módulo SISBAJUD×STA (workflow de agentes + verificação adversarial)

Pergunta do dono: com a cabine STA padronizada, o fluxo fica 100% automatizado ou depende de upload manual? **Resposta comprovada com evidência arquivo:linha:**

- **O Core JÁ tinha recepção automática** (`StaHandler.maybe_ingest_sisbajud` consome `monetarie.sta.file.received` do sistema JUD e chama o MESMO `Sisbajud.ingest` do upload manual), processamento sem gate manual (preview é opcional), **geração automática do 5302/5309** (`RegulatoryFile` `generated`) e **análise automática das validações do BCB** (5303/5304/5310/5331 → `Validacao.Consumer` marca `confirmed`/`rejected`).
- **O gap estava na cabine STA (`sta/`):** o poller inbound baixava o arquivo do BCB e **jogava o conteúdo fora** (evento só com metadados; o Core exige `content` base64 e pulava em silêncio — contrato fixado em teste), e o sistema `JUD` não estava no polling default (e qualquer rota HTTP configurada derrubava o default inteiro).
- **Envio deliberado por design** (botão; cron previsto e nunca implementado; `GenerateReportJob` recusa SISBAJUD).
- Higiene descoberta: tabelas mortas `judicial_pending_files`/`judicial_courts`/`judicial_info_requests`/`judicial_file_validations` (só migrations Sprint 0, zero código); `Sisbajud.RetentionWorker` documenta cron que NUNCA foi registrado em crontab (não roda); handler legado morto `Judicial.NatsHandler`.

## 3. Implementação do ciclo (TDD em TODAS as pernas, review adversarial em 2 rodadas)

### `aeaee2c0` — fecha o gap (cabine 10/10, core 3/3)
- **Cabine:** `file.received` embeda `content` base64 + `content_encoding` para sistemas da allowlist (`JUD`) até 600KB (teto protege o payload NATS de 1MB; acima sai sem content com warning, fallback = upload manual). **Bug pré-existente corrigido:** retry bem-sucedido (download/parse/rota) nunca publicava `file.received` — agora publica (com conteúdo re-baixado onde há download). `Poller.resolve_system_ids`: JUD no default e SEMPRE pollado (união com rotas).
- **Core:** flag `SISBAJUD_AUTO_SEND_ENABLED` (ver decisão do dono acima); `Sisbajud.maybe_auto_send` fora do lock de idempotência, fail-soft, nunca em `:already_processed`/validações.
- Nota do review: `Files.create_protocol` da cabine JÁ emitia um `file.received` precoce sem content (pré-existente; Core imune — skip sem content + ingest idempotente).

### `65330019` — durabilidade da ingestão (6/6; suites 96/0 + 291/0)
A fase final do review adversarial (14 agentes) confirmou 2 ALTA: ingestão inline no consumer NATS era **at-most-once** (raise transitório = remessa 5301 com prazo legal de 24h perdida sem redelivery, cabine já confirmou ao BCB) e **consumida em duplicata** pelos 2 consumers do Core (advisory lock bloqueante + timeout 15s → crash + mailbox perdido). Fix: `SisbajudStaIngestWorker` (Oban `:regulatory`, `unique` por `source_hash`, 10 tentativas com backoff) — o handler só detecta + enfileira. Em `:inline` (test) o efeito fim a fim é idêntico (contrato existente passou sem mudanças).

### `059b045a` — consumo durável + watchdog (10/10; suíte 387/0)
- **`StaConsumer` migrado para `DurableConsumer`** (o P0.3 que PIX/SPB adotaram em 09/07): push durável `core-sta-consumer` no stream `MONETARIE_STA` (`file.*` + `protocol.processed`), ACK pós-processamento, NAK+redelivery pelo SERVIDOR, DLQ limitada, dedup em 3 camadas (MsgIdentity/MessageDedup + unique Oban + Idempotency por hash). O handler PROPAGA falha de enqueue (`{:error}` → NAK) e o rescue amplo saiu de propósito (engolir = ACK = matar a redelivery).
- **Dono único:** o Consumer unificado saiu de `monetarie.sta.>` (rotas removidas) — levantamento exaustivo confirmou zero subjects órfãos (regulatory.upload/sisbacen/accs são Core→cabine).
- **`SisbajudDeliveryWatchdog`** (cron `*/10` em runtime.exs + config.exs): SISBAJUD `delivering` estagnado >60min → `failed` (reenviável + visível + `error_message`); com auto-send ON re-dispara `send_via_sta` com teto de 3 (`metadata.watchdog_resend_attempts`) — nunca loop infinito. Janela de 60min por decisão do review (corrida confirmação-tardia×reenvio vira improvável; dano seria só retransmissão duplicada, que o BCB tolera).
- Notas do review acatadas/registradas: `Regulatory.update_file_status` com `%{metadata: ...}` é REPLACE, não merge (armadilha p/ futuros callers; o watchdog constrói por `Map.put` sobre o mapa atual — nada se perde); checagem de status remoto na cabine antes do reenvio fica como melhoria futura.

### `d757b080` — CAS + guarda terminal (fase final do review, 2 MEDIA; 5/5, suíte 392/0)
A fase final do review do watchdog confirmou 2 corridas de estado, corrigidas: (a) **demoção com compare-and-swap** (`demote_if_still_delivering/2`: `UPDATE ... WHERE status='delivering'`, só prossegue com 1 linha; incremento do contador idem com `WHERE status='failed'`) — a confirmação da cabine chegando entre o SELECT e o UPDATE não sobrescreve mais `delivered`/`confirmed` com `failed`; (b) **guarda de estado terminal no StaHandler**: evento de ciclo de vida tardio (ex.: `uploaded` da transmissão antiga) é ignorado com log quando o RegulatoryFile já está `confirmed`/`rejected` (fim do ciclo BCB). Terceiro achado (BAIXA, sem mudança): ruído transitório de auditoria duplicada só na janela do PRIMEIRO deploy do consumo durável (dedup do lado antigo usava chave por-entrega); dinheiro imune.

### `c0fb3def` — review do review (1 BAIXA; suíte 393/0)
O review adversarial do próprio 6e46a518 achou 1 regressão BAIXA na guarda: o descarte do `uploaded` tardio era INTEIRO, e esse evento é o único escritor de `sta_protocol`/`sta_delivered_at` — se a validação 5303/5310 confirmasse antes do consumo do evento (redelivery NAK, replay de DLQ, upload manual do 5303 em backlog), o arquivo ficava `confirmed` sem protocolo STA para sempre. Fix: em estado terminal o `uploaded` ABSORVE só os dados (preservando valores presentes + timeline) e reafirma o status. Follow-ups latentes registrados (pré-existentes, fora do escopo): mesma classe de regressão sem guarda no `update_cadoc_submission` (Submission CADOC); TOCTOU residual read-then-write da guarda; `Repo.get!` do watchdog vs delete concorrente da tela.

## 6. Incidente de infra LOCAL (diagnosticado e resolvido na sessão)
Um restart do Docker daemon às ~15:29 BRT parou graciosamente TUDO que não tinha restart policy: `monetarie-pg`, `monetarie-redis`, `monetarie-nats` e a infra de outros projetos (cecresa/avivpay/owem). Nossos apps voltaram sozinhos (`unless-stopped` desde a criação). Resolução: religados pg/redis/nats nossos + `docker update --restart unless-stopped monetarie-pg monetarie-redis monetarie-nats tigerbeetle`; restart limpo dos 4 apps; validado vivo (health 200 em 4002/4003/4010/5180; os 3 consumers duráveis do core promoveram: PIX, SPB e STA). NATS de outros projetos seguem parados de propósito (não são nossos).

## 4. GOTCHA NOVO de ambiente local (importante)

**O core-api local PRECISA de `-e NATS_STREAM_REPLICAS=1`** (além do `--security-opt seccomp=unconfined`): o `StreamSetup` default pede 3 réplicas e o `monetarie-nats` local é nó único (err 10074) — **NENHUM stream MONETARIE_* era criado localmente** e TODOS os consumers duráveis (PIX/SPB/STA) caíam em modo degradado (sub plana sem redelivery) em silêncio, desde sempre. Consertado nesta sessão (container recriado com a env; `docker inspect` futuro herda). Prova viva: `[STA Consumer] consumo duravel ativo (core-sta-consumer@MONETARIE_STA)` — inclusive exercitando o retry de bind vencendo a corrida com o StreamSetup. Registrado na memória Serena `dev_commands`.

## 5. Roteiro de teste quando a credencial STA chegar

1. Conectar a cabine STA no ambiente (credenciais BCB) — o envio manual do 5302 fecha ponta a ponta no dia 1 (botão → NATS → cabine → BCB → eventos fecham `delivering`→`delivered`→`confirmed`).
2. Ciclo automático completo com AJUD5301 real: poller JUD baixa → evento com content → worker durável ingere → ordens processadas (hold TB) → 5302 `generated` → (botão OU flag ON) envio → validações 5303/5310 confirmam sozinhas.
3. Decidir `SISBAJUD_AUTO_SEND_ENABLED` por ambiente (default OFF = operador valida).

## PENDÊNCIAS / FOLLOW-UPS

0. **PRÓXIMO GRANDE MANDATO (inalterado):** backup contábil + plano de contas OFICIAL → migração + batimento ao centavo vs balancete Maio/2026 + definição das contas corretas (destrava Camada B/Fase 3). AGUARDAR o backup.
1. **PRÉ-CONDIÇÃO DE DEPLOY (inalterada, crítica):** prod roda judicial em base_units; branch em centavos → reconciliar dados judiciais de prod ANTES de deployar.
2. **Cron de janelas do manual SISBAJUD** — pendente de decisão do dono e das janelas oficiais como insumo (o envio automático atual é imediato).
3. **Melhoria futura do watchdog:** checar status remoto na cabine antes de reenviar (hoje mitigado pela janela de 60min).
4. **Higiene SISBAJUD** (sessão futura): dropar tabelas mortas (`judicial_pending_files`/`courts`/`info_requests`/`file_validations`), registrar ou remover o cron do `RetentionWorker`, remover `Judicial.NatsHandler` morto.
5. **Baseline de testes da cabine STA:** 4 falhas pré-existentes machine-dependent (RouteTest 2, RouterTest 1, LoaderTest 1 — fixtures locais); da cabine SPB: 82 (fixtures `md/` + `api_gateway` não versionados, só existem na máquina do Luiz).
6. Fase 3 COSIF (`PostingEngine`), Lifeline do Oban, gap do extrato PIX-out, HML `validate_account`×stream — inalterados das sessões anteriores.

## Fontes de verdade

- Este handoff (mais recente; substitui o de 2026-07-13 como retomada).
- Anterior: `docs/handoff/2026-07-13-sessao-fase1-fase2-contabilizacao-merge-main-handoff.md` (contabilização PIX Fase 1+2).
- SISBAJUD histórico: `docs/handoff/2026-07-09-sessao-sisbajud-handoff.md` + fixes de 09/07.
- Memórias: auto-memory `pendentes-sisbajud-pos-fix.md` (atualizada com 14a/14b/14c); Serena `dev_commands` (gotcha NATS_STREAM_REPLICAS), `sisbajud_estado_atual`, `project_overview`.
