Pular para conteúdo

0024 — Manifesto de remessa

Status: Aprovado · Responsável: Gustavo Madruga · Atualizado em: 2026-09-09 · Decidido em: 2026-09-09

Contexto

O ERP rejeita filho sem pai, em silêncio e de forma terminal (1XX), e o coletor lê só 000/9XX — a linha rejeitada fica invisível para sempre. O ingest é atômico no Postgres, mas essa atomicidade se perde na fronteira do PowerSync: linhas da mesma transação chegam ao cliente em checkpoints diferentes.

Três formatos do mesmo defeito, todos medidos em produção em dois dias:

pai ausente pedido evidência
propriedades (cliente) 260079421, 08-set 120-Cliente CPF 072.508.149-07 não cadastrado.
contratos (pedido) 260079618, 09-set 120-Pedido (10738.24) não cadastrado.
estoque (produto) 260078864, 09-set 120-Produto para o pedido (…) não cadastrado.

A cascata é 1:N e silenciosa: no 260079421 uma linha ausente produziu nove terminais; no log inteiro, 53. E o coletor monta um arquivo com vários pedidos, então a completude tem de ser avaliada por unidade de negócio, nunca pelo arquivo.

Do lado do cliente, "a linha ainda não chegou" e "a linha não existe" são indistinguíveis — logo nenhuma regra local resolve. O Integrador, no instante do apply, já sabe o conjunto exato.

Decisão

Declarar a unidade de entrega na origem: integracao_remessa + integracao_remessa_item, criadas na mesma transação do apply e replicadas junto com as linhas.

  • Uma remessa por grupo com dependência (≥2 linhas ligadas), não por push. Linha solta segue coletável — o manifesto acrescenta garantia, nunca remove, e é o que torna o rollout não-quebrante.
  • Uma remessa viva por (origem, chave_negocio) — invariante de banco (índice único parcial WHERE status <> 'ENTREGUE'). É o que faz o resgate fechar: o re-push reusa a travada e o re-arme a reabre, sem colisão.
  • Estado derivado por UMA função, chamada do apply, do soft-delete, do write-back e do re-arme. Uma fonte só é o que impede o drift de status, total_itens, bloqueado e resolvido.
  • O re-arme é por remessa, não por tabela: alcança o pai e a remessa de cliente.

Alternativas descartadas

Estado 002 ("aguardando pai") na linha. Foi o primeiro desenho. Exigiria inventar um código numa coluna compartilhada com o ERP — homologação com o dono do pAbast — e punha a elegibilidade na linha, não na unidade. O manifesto a torna desnecessária.

Gate de FK no cliente. Cinco regras hoje, quinze no fluxo completo, cada uma escrita por quem talvez não conheça a semântica — e regra errada trava produção em silêncio. Pior: não resolve o caso da linha que ainda não chegou, que é o caso dos três incidentes. O manifesto é O(1) no cliente.

bloqueado deduzido pelo cliente. O princípio descartado era o cliente inferir dependência, não modelar dependência. O servidor já tem o grafo; ele marca a flag e o cliente só a lê — a precisão do gate de FK sem o acoplamento.

Consequências

O valor depende de três repos. Este declara; o integrador-client (.ia/003 dele) decide o que entra no arquivo; o maxsul/powersync (.ia/002 dele) transporta; o maxsul-pied (.ia/009 dele) reconcilia. Ordem de deploy: este repo primeiro (a V27 cria as tabelas que as sync rules referenciam), depois powersync, cliente e pied. Qualquer ordem é segura — os três degradam para o comportamento de hoje —, mas só esta entrega valor.

Uma janela fica aberta, e é do consumidor. Linha que chega antes da remessa dela é, para o cliente, indistinguível de linha solta. Fecha-se com carência de 3 ciclos no integrador-client; o servidor não tem como resolver, porque a remessa nasce na mesma transação e quem as separa é o PowerSync.

A derivação roda no caminho quente do write-back — um PATCH por linha, cada um recalculando as remessas que a contêm. Dezenas de linhas por remessa no volume atual; medir antes de otimizar.

As tabelas levam prefixo integracao_, convenção nova neste repo: as três de controle existentes (request, push_enviada, mensagem_m2m) não têm prefixo. Adotada de propósito — num banco que tende a ser réplica do ERP, a fronteira precisa estar no nome. É candidato a §6: a casa não normatiza nome de tabela auxiliar em base-espelho.

Verificação

Regressão dos três incidentes por número de pedido, contra Postgres real: a remessa inclui o pai em cada um dos três casos, e o re-arme alcança o pai — que era o beco do desenho anterior. Mais a derivação (pai bloqueia filho, folha não bloqueia irmã, soft-delete e órfão resolvem) e o contrato do endpoint (bloqueado separando causa de colateral).