Decisions
Estas decisões refletem o estado canonical da Versão Atual (07/05/2026). Cada ADR resume o que, por que e status atual. Pendências e gaps do audit ficam ao final.
ADR-1 · Versão Atual: AABC-only
Seção intitulada “ADR-1 · Versão Atual: AABC-only”- What — Integração exclusiva via aplicativo Omie AABC (CNPJ 50.162.682/0001-07). LNG fora do escopo desta versão.
- Why — Foco MVP. Reduz superfície de teste e evita conflito de regras fiscais entre orgs.
- Status — Decidido 07/05/2026 · Confluence
3813310465.
ADR-2 · Mínimo Bubble
Seção intitulada “ADR-2 · Mínimo Bubble”- What — Bubble dispara exatamente 1 trigger automático (
POST /v1/orders/{bubble_order_id}/sync-omieem “Pedido criado”) e expõe N botões backoffice/website chamando endpoints do backend (retry,cancel-nf,mark-nf-manual,approve-billing-hold,override-day25,transfer-cc,use-cc). Bubble não recebe webhook Pagar.me nem chama Brasil API. - Why — Bubble continua dono dos dados e do front; backend é orquestrador. Não recriar funcionalidades existentes; centralizar side-effects e regras fiscais onde há capacidade de teste e observability.
- Status — Decidido · canonical em AGENTS.md (“Premissa Mínimo Bubble”).
ADR-3 · Sync síncrono atômico no MVP
Seção intitulada “ADR-3 · Sync síncrono atômico no MVP”- What — Bubble chama
sync-omiesíncrono e espera resposta. Não há outbox pattern. Backend escreve status writeback de volta no Bubble Data API dentro da mesma transação lógica. - Why — Simplicidade. Evita consistency lag visível ao backoffice. Deferir outbox até segundo consumer aparecer ou até volume justificar.
- Status — Decidido para MVP · revisar pós primeiro produção.
ADR-4 · Status duplos independentes
Seção intitulada “ADR-4 · Status duplos independentes”- What — Pedido carrega
status_pedido∈ cancelado (3 valores) estatus_faturamento∈ faturamento_manual (9 valores), evoluídos independentemente. - Why — Lifecycle de pagamento ≠ lifecycle de faturamento. Carta de crédito, holds manuais, regra dia 25 e cancelamento de NF têm transições próprias que não mapeiam 1:1 em estado de pedido.
- Status — Decidido · Confluence
3812950022(Modelo de Estados).
ADR-5 · HubSpot Fase 2 descartado
Seção intitulada “ADR-5 · HubSpot Fase 2 descartado”- What —
BLU-766(integração HubSpot Fase 2) removido do escopo da Versão Atual. - Why — Scope reduction. Sem caso de uso bloqueante para MVP.
- Status — Mover para
wontfix-mvp· ação pendente no board.
ADR-6 · Consolidação de pedidos descartada
Seção intitulada “ADR-6 · Consolidação de pedidos descartada”- What —
BLU-781(consolidação de pedidos) fora do escopo da Versão Atual. - Why — Complexidade incremental sem caso de uso confirmado no MVP. Reabrir se demanda comercial surgir pós-launch.
- Status — Mover para
wontfix-mvp· ação pendente no board.
ADR-7 · Pagar.me webhook direto ao backend
Seção intitulada “ADR-7 · Pagar.me webhook direto ao backend”- What —
POST /webhooks/pagarmeno backend, HMAC mandatory. Idempotência porcharge_id. Bubble não processa webhook. - Why — HMAC verification e idempotência cabem no backend; Bubble não tem capacity nativa para assinatura HMAC nem para gating de retry com janela de 31min do rate-limit Omie.
- Status — Decidido · Confluence
3812327443(Módulo Pagar.me).
ADR-8 · Brasil API CNPJ chamado pelo backend
Seção intitulada “ADR-8 · Brasil API CNPJ chamado pelo backend”- What — Backend chama Brasil API CNPJ lookup e escreve resultado no Bubble via Data API.
- Why — Rate limit e cache no backend; Bubble não é HTTP client capable para integração externa com retry policy e TTL.
- Status — Decidido · Confluence
3813539856(Mapeamento Payload).
ADR-9 · Reconciler autônomo CDC pull com cursor
Seção intitulada “ADR-9 · Reconciler autônomo CDC pull com cursor”- What — Backend roda como reconciler autônomo com cron ARQ
cdc_tick(hourly,0 * * * *) que puxa Orders modificados via Bubble Data API com cursor (GET /api/1.1/obj/order :search constraint modified_date > last_cursor). Cursor persistido em Postgres backend (tabelabubble_sync_cursor). Shadow store local (order_shadow) detecta state delta sem segundo round-trip. Reconciler decide viabilling_service.decide_billing+utils/dia25.compute_billing_datee executa sequência Omie. Único endpoint Bubble-facing:POST /v1/orders/{id}/sync-omie(manual force re-sync, mesmo handler do tick parametrizado pra 1 order). Endpoints/retry,/cancel-nf,/mark-nf-manual,/override-day25,/payment-confirmedremovidos — todas as ações viraram edições em campos do Order Bubble que o reconciler observa. - Why — Tentativas iniciais (Opção A: webhook push Bubble→backend; Opção B: cron polling 60s; Opção C híbrido) todas envolvem workflows Bubble novos acoplados ao backend (frágil, sem retry nativo, falha silenciosa) ou gasto desnecessário de Bubble Data API (1440 Searches/dia em polling 60s). Throughput real é ~10 pedidos/dia — fluxo fiscal tolera latência horária. CDC pull com cursor: zero workflow Bubble novo, 24 calls/dia, cursor garante avanço monotônico, idempotente e reentrante. Webhook Pagar.me continua no Bubble (legado HMAC funcionando) — reconciler descobre
status_pedido = confirmadoviamodified_dateno próximo tick. - Status — Decidido 2026-06-09 · pendente implementação (Slice F) · referência: Integration · Bubble × Backend e Omie Sync State. Implementação requer: (a) Alembic migration
bubble_sync_cursor+order_shadow, (b) job ARQcdc_tick(0 * * * *) + função core_reconcile_one(order), (c) handlerPOST /v1/orders/{id}/sync-omiechamando_reconcile_onedireto (sem mexer no cursor), (d) deprecation dos endpoints/retry,/cancel-nf,/mark-nf-manual,/override-day25+ remoção do OpenAPI, (e) novos campos no Order Bubble (billing_scheduled_date_override,faturamento_manual,omie_nfse_numero_manual,omie_nfse_codigo_manual) que substituem os antigos endpoints. Migração futura (V2) pode mover webhook Pagar.me para o backend e fazer Bubble consumir viaOmie Sync State— sem ROI hoje.
Histórico — opções descartadas
Seção intitulada “Histórico — opções descartadas”| Opção | Por que NÃO |
|---|---|
| Webhook push Bubble→backend (Opção A) | Workflow Bubble pode falhar silenciosamente; sem retry nativo; acopla backend a config no-code. |
| Cron polling 60s (Opção B) | 1440 Searches/dia desnecessários pra throughput ~10 pedidos/dia. |
| Híbrido push + reconcile 15min (Opção C original) | Mantém dependência de workflow Bubble + endpoint /payment-confirmed extra. CDC pull pure é mais simples e dispensa workflow. |
Pendências em aberto
Seção intitulada “Pendências em aberto”- P11 · formato
vendedorna O.S. Omie (consultar/geral/vendedores/) — Aguardando Luiz - P12 · estratégia homologação NFS-e (acesso 7d ambiente demo) — Aguardando Luiz
- PAGARME-1 ·
PAGARME_WEBHOOK_SECRETrotacionado? — Aguardando Luiz - BUBBLE-1 · Bubble tem
pagarmedetails+pagarmewebhookcallno schema — substitui ou convive com handler atual? — Aguardando Luiz - OMIE-1 · cache strategy
/servicos/listaservico/(TTL? refresh diário?) — Discovery - BLU-783 · rate-limit Omie discovery existe, falta ticket filho de implementação — Bloqueia retry policy
Gaps OrderToBillingFlow
Seção intitulada “Gaps OrderToBillingFlow”Top-5 gaps identificados no audit docs/reports/order-to-billing-flow-audit.html, em ordem de implementação:
- Gap 4 (1h) — Unificar enum status writeback Bubble:
sync_service.py:430escreve"NF emitida"(PT) vsinvoice_service.py:222escreve"invoiced"(EN). Confirmar Option Setns_statuscanonical antes de unificar. Quick win - Gap 2 (1d) — Alembic migration adicionando
billing_scheduled_date+billing_triggerembilling_schedule. Pré-requisito Gap 1 - Gap 1 (1d) —
run_billing_jobdeve chamarInvoiceService.emit_invoice(depende de Gap 2). Bloqueia faturamento agendado - Gap 5 (4h) —
BanErrorespecífico → HTTP 503 comRetry-After: 1860(31min). Higiene de retry - Gap 3 — outbox pattern. Defer pós-MVP (alinhado ADR-3).
Roadmap Segurança Pós-MVP
Seção intitulada “Roadmap Segurança Pós-MVP”Decisões adiadas conscientemente. Registradas pra revisita após MVP estabilizar.
Contexto MVP (decidido 2026-05-05)
Seção intitulada “Contexto MVP (decidido 2026-05-05)”Auth Bubble → Backend:
- Static bearer token em header
Authorization: Bearer <token> - Header obrigatório
X-Request-Timestamp(unix epoch ms) com janela de 5 min - Header obrigatório
Idempotency-Key(UUID por request) - Token armazenado no Bubble API Connector (server-side) e em
.envdo backend - Rotação trimestral manual aspiracional —
.env.exampledocumenta mecanismo, sem automação
Auth Backend → Bubble:
BUBBLE_API_KEYbearer já existente, rotação anual manual
Pendências fase 2 (não-MVP)
Seção intitulada “Pendências fase 2 (não-MVP)”| # | Item | Avaliado em MVP — descartado por | Trigger pra revisitar |
|---|---|---|---|
| 9.1 | HMAC body signature (X-Signature: HMAC-SHA256(body, secret)) | Bubble no-code não tem builtin HMAC; exige plugin/custom code = scope creep | Detecção de replay/MITM em produção; ou Bubble lançar HMAC nativo |
| 9.2 | Rotação automatizada via secret manager (AWS Secrets Manager / HashiCorp Vault) | MVP roda em infra simples, sem secret manager. Bubble API Connector exige update manual mesmo com manager. | Adoção de secret manager pela infra; ou compliance audit |
| 9.3 | IP allowlist complementar a bearer | Bubble usa AWS dinâmico, sem range estável publicado. User-Agent filter descartado (ruído, fácil falsificar). | Bubble publicar lista oficial de IPs egress; ou compliance audit |
| 9.4 | mTLS | Bubble não suporta client cert | Bubble lançar suporte; ou requisito B2B explícito |
| 9.5 | JWT short-lived com claims (per-call, exp 60s) | Bubble não gera JWT signed sem plugin | Plugin oficial Bubble ou Bubble flow code |
| 9.6 | Audit de autenticação (log de tentativas falhas, alerta após N) | MVP escopo small, sem SOC | Volume real justifica monitoramento ativo |
Backlog deferido — pós-smoke green
Seção intitulada “Backlog deferido — pós-smoke green”Itens decididos na arquitetura mas fora do escopo do primeiro smoke test green (happy path PJ AABC sync-omie). Cada um vira teste vertical posterior ou tarefa de infra dedicada.
| # | Item | Decisão arquitetura | Quando endereçar |
|---|---|---|---|
| 10.1 | Postgres + Alembic para action_log, idempotency_cache, omie_id_map, omie_project_map | MVP smoke usa in-memory dict — comportamento vem primeiro, persistência depois | Após smoke green; teste vertical “restart preserva idempotency cache” |
| 10.2 | Retry policy 1s/5s/30s (sync inline) + is_retriable policy | Decidido. Smoke só exercita happy path. | Teste vertical: “Omie 503 transitório → retry → green” |
| 10.3 | Timeout per-call Omie 10s | Decidido. Smoke não exercita timeout. | Teste vertical: “Omie hang → 10s timeout → retry” |
| 10.4 | CI cron diário pra drift detection schema Bubble (re-fetch /meta vs commitado) | Drift on-demand pra MVP. CI cron descartado por simplicidade. | Quando schema Bubble começar a mudar com freq |
| 10.5 | Codegen automation (pre-commit hook + CI gate) | MVP roda manual python -m bubble_sdk.codegen | Quando codegen quebrar build silenciosamente ao menos 1× |
| 10.6 | Rotação automatizada bearer token via secret manager | Pendência seção §Roadmap Segurança | Adoção de secret manager |
| 10.7 | Circuit breaker (5 falhas Omie consecutivas em 10min → 503 fast-fail por 1min) | Não-MVP. Decidido. | Após observar pattern real de falhas |
| 10.8 | Endpoints manuais cancel-nf, mark-nf-manual, retry | Decididos. Smoke cobre só sync-omie. | 1 teste vertical por endpoint |
| 10.9 | Validação timestamp window 5min + Idempotency-Key obrigatória em prod | Decidido. Smoke desabilita validação (BUBBLE_AUTH_REQUIRED=false). | Teste vertical: “request timestamp >5min → 401” |
| 10.10 | option.* como Literal (após enum values pendência ns_status / nf_invoice) | Codegen gera str no MVP. Regenera com Literal quando enum values vierem. | Quando dev Bubble preencher option types |
| 10.11 | Dispatcher AABC/LNG cross-check (validar mismatch payment_special_condition × empresa escolhida) | Decidido — pode emitir 422 erro_roteamento | Teste vertical: “PJ flexível sem empresa escolhida → 422” |
| 10.12 | omie_id_map cache cross-request (2ª chamada usa código cacheado) | Decidido — PK composta. Smoke limpa state entre testes. | Teste vertical: “2ª sync mesmo cliente → não chama IncluirCliente” |
| 10.13 | omie_project_map cache cross-request | Decidido | Teste vertical: “2ª sync mesmo evento → não chama IncluirProjeto” |
| 10.14 | Mock Bubble UI (L2 + L3) | Não-MVP — só L1 HTTP-only stateful | Quando QA manual exigir |
| 10.15 | Frontend Bubble real chamando API | Bubble dev separado, fora do backend | Após backend smoke green |
| 10.16 | Deploy infra real | Stack decidida no AGENTS.md mas não scaffolded | Após primeiros 3-4 testes verticais |