Project recap · janela 14d · 2026-04-25 → 2026-05-08

Plataforma B2B de cobrança
conversacional, em prod.

IA que negocia dívidas via WhatsApp 24/7, controlada por feature flag, auditada por LangGraph, observada por Langfuse. ~99% feature-complete; 101 commits em duas semanas; smoke real validado em produção há horas.

branch launch/v2 HEAD 196d8aa suite 403/403 1 dev solo (Edilson) · 1 sócio (Pablo, owner Sentry)
§ 01 · IDENTIDADE

O que isto é hoje, em uma frase

Não é o blurb do README — é o instantâneo do estágio.

Sistema FastAPI/Python que recebe mensagens via webhook Twilio, redireciona pelo estado da conversa, e — em NEGOTIATION — executa um pipeline cascata FAQ retrieval (Qdrant) → Sonnet 4.6 planner → domain agent fallback, com PII redaction síncrona, output guard, persistência de propostas (PROPOSAL_COHERENCE) e CRM conversacional vetorial (Supabase pgvector). Pagamento real via Asaas (PIX) com retry queue Redis ZSET. Observabilidade via Langfuse + Sentry (release attribution desde hoje). Toda orquestração CrewAI legacy substituída por LangGraph 1.1.10 declarativo no estado mais crítico, atrás de feature flag default-OFF.

Estágio
~99%
V2 feature-complete; demoável a clientes pagantes desde 2026-05-05
Commits 14d
101
Inclui 25+ commits hoje implementando AUDIT_2026-05-08
Suite local
403
Tests passing · 39 novos vs baseline 364 da Fase 1 LangGraph
Versão CrewAI
1.14.4
Pinned; será removido em CREWAI_TO_LANGGRAPH Fase 5 (~2-3d)
Versão LangGraph
1.1.10
Subpacote em services/conversation/langgraph/ · 5 arquivos
Prod state
healthy
5 containers Up · API 200 · Qdrant 1.15.5 · Mongo 7.0.31
§ 02 · ARQUITETURA

Sistema como existe agora

Não a arquitetura idealizada — o que está rodando às 22h53 de 2026-05-08.

flowchart TD
    classDef edge   fill:#e8eef4,stroke:#3d6e94,color:#2a4d6e,stroke-width:1px
    classDef core   fill:#fbf9f4,stroke:#1c2330,color:#1c2330,stroke-width:2px
    classDef llm    fill:#e7efe9,stroke:#2f5d4a,color:#2f5d4a,stroke-width:1px
    classDef store  fill:#f5ead4,stroke:#8a5a1f,color:#8a5a1f,stroke-width:1px
    classDef ext    fill:#f4e1d8,stroke:#a04a35,color:#a04a35,stroke-width:1px
    classDef obs    fill:#e8eef4,stroke:#3d6e94,color:#3d6e94,stroke-width:1px,stroke-dasharray: 5 3

    User[Devedor
WhatsApp]:::edge Twilio[Twilio
WhatsApp BR]:::edge User -- mensagem --> Twilio Twilio -- webhook POST --> NGINX[nginx
+ Caddy HTTPS]:::edge NGINX --> APP subgraph CORE [API · FastAPI 0.136.1 · Sentry release 196d8aa] APP[webhook handler
+ middleware Sentry scope]:::core ROUTER[conversation_router
state-aware]:::core APP --> ROUTER ROUTER -- ONBOARDING/AUTH/PLANS
SALES/CONTRACT/CHECKOUT --> CREWAI[domain_agent
CrewAI 1.14.4]:::core ROUTER -- NEGOTIATION --> FLAG{flag
MIGRATE_NEGOTIATION_
TO_LANGGRAPH}:::core FLAG -- false default --> IMP[handle_negotiation_flow
imperativo]:::core FLAG -- true --> LG[run_graph
LangGraph 1.1.10]:::core end subgraph PIPELINE [Pipeline NEGOTIATION · FAQ → Sonnet → fallback] PERC[perceive
PII redact + intent
+ sentiment via Haiku 4.5]:::llm DOM[domain_agent_run
template-based]:::llm BUILD[build_planner_ctx
+ proposal_history
+ similar_interactions]:::llm FAQ[faq_check
Qdrant 1.15.5]:::llm SONNET[plan_sonnet
Claude Sonnet 4.6]:::llm FINAL[finalize_telemetry
output_guard]:::llm IMP --> PERC LG --> PERC PERC --> DOM --> BUILD --> FAQ FAQ -- hit similarity ≥ 0.60 --> FINAL FAQ -- miss --> SONNET SONNET -- success --> FINAL SONNET -- empty --> FINAL end subgraph DATA [Camada de dados] MONGO[(MongoDB 7.0.31
contractors · debtors
· negotiations · audit_log
TTL 5y LGPD ✓)]:::store REDIS[(Redis 7.4
locks · payment retry ZSET
· msg buffer)]:::store QDRANT[(Qdrant 1.15.5
30 entries FAQ
cosine HNSW)]:::store SUPA[(Supabase pgvector
debtor_interactions
1536-dim cosine)]:::store end BUILD --> SUPA FAQ --> QDRANT SONNET --> MONGO FINAL --> MONGO APP --> REDIS subgraph EXT [Externos] ASAAS[Asaas
multi-tenant subaccount
PIX + boleto]:::ext STRIPE[Stripe
checkout legacy]:::ext WHISPER[OpenAI
gpt-4o-mini-transcribe
language=pt]:::ext EMBED[OpenAI
text-embedding-3-small]:::ext end SONNET --> ASAAS FINAL --> Twilio Twilio -- audio --> WHISPER WHISPER --> APP BUILD --> EMBED EMBED --> SUPA EMBED --> QDRANT subgraph OBSERV [Observabilidade] LANGFUSE[Langfuse 4.5.1
trace negotiation_turn
+ spans manuais]:::obs SENTRY[Sentry 2.59
release tracking
+ Anthropic/Redis/Pymongo
integrations]:::obs end PERC -.-> LANGFUSE SONNET -.-> LANGFUSE FINAL -.-> LANGFUSE APP -.-> SENTRY Twilio --> User2[Devedor recebe
resposta]:::edge

↑ Diagrama interativo · scroll para zoom · arrastar para mover · botão ↗ abre em janela cheia

§ 03 · ATIVIDADE

14 dias, 4 frentes em paralelo

Migração arquitetural, hardening de segurança, observabilidade e UX.

Migração CrewAI → LangGraph
Fase 1 ✓

Pipeline NEGOTIATION encapsulado em grafo LangGraph declarativo, controlado por MIGRATE_NEGOTIATION_TO_LANGGRAPH (default false). 6 mensagens reais validadas em prod via Sandbox; rollback drill em ~50s.

  • c136312 langgraph subpackage (state · nodes · graph · runtime)
  • 698ea8b 8 testes T-1..T-7 cobrindo cada path
  • edcacc5 early-branch em handle_negotiation_flow
  • fc76977 instrumentation log marker [LANGGRAPH]
  • d2139eb ship + archive + memory
Hardening · Auditoria 2026-05-08
5 pacotes

Auditoria via 4 subagents paralelos contra docs oficiais. Top 10 ações priorizadas; 5 pacotes shipped no mesmo dia (cleanup, Sentry, TTL audit, Qdrant bump, Redis lock).

  • caba17f AUDIT_2026-05-08.md baseline
  • 5261fd9 remove 4 OTel deps (dead-weight)
  • 4ae48ec TTL 5y em domain_audit_logs (LGPD)
  • 27bfcb0 Sentry release + AnthropicIntegration
  • 19f69fa Qdrant 1.12.4 → 1.15.5 (snapshot + recreate)
  • c40b3e7 redis.lock.Lock ownership-safe
Bug fixes silenciosos descobertos
2 críticos

Smoke #3 do audit revelou que domain_audit_logs em prod tinha apenas _id_. $ne em partialFilterExpression não é suportado pelo MongoDB e abortava todo o try/except.

  • 196d8aa partialFilterExpression sem $ne
  • b8156a5 _get_anthropic_embedding broken path removido
  • fadad42 Pydantic model_validate (Rust core)
  • daa7b85 Whisper language=pt (anti-alucinação)
Frontend admin · Métricas CRM
Backend + UI

Endpoint GET /api/admin/crm/metrics retornando 8 métricas agregadas, página React consumindo com TanStack Query, Layout menu Administração → Métricas CRM (admin only). Smoke real via Playwright validou render em prod.

  • b802874 backend admin_crm endpoint + 5 testes
  • 2198244 frontend AdminCRMMetrics.jsx + Layout entry
  • cb7ddee adaptive threshold search_similar 0.70/0.55
Refactors mecânicos
~960 LOC

STATE_MANAGER_DECOMPOSITION (1930→1234, -36%). PROJECT_STRUCTURE_REORG (83 arquivos em 8 subpastas). handle_auth_flow decomposto em 1 dispatcher + 4 helpers (29 testes).

  • eca07a2 handle_auth_flow refactor + 2 fixes (1B+2C)
  • c7d4063 search_similar integrado no PlannerContext
  • 49e9535 requirements pin to prod versions
  • 5cb8115 cleanup LangChain 0.x dead code (-57 LOC)
SDD workspace hygiene
+regras

Archive stale _LANGGRAPH_MIGRATION_v1_NOT_APPLIED marcado e depois deletado. Pre-DEFINE checklist agora obrigatório no CLAUDE.md. 7 features shipped foram migradas de features/ para archive/.

  • 4377476 mark stale archive + pre-define rule
  • e1913b0 archive 7 shipped feature docs
  • fb32a5b retroactive ship MULTI_TENANT_ASAAS
  • c0bb7c3 remove stale V1 langgraph archive
§ 04 · DECISÕES

A racionalização que evapora primeiro

Mineradas dos commits, audit doc e conversas das últimas 14 dias.

2026-05-07 · ARQUITETURAL

ADR-2 do CREWAI_TO_LANGGRAPH: side effects FORA do grafo

Grafo LangGraph decide resposta + telemetria. Caller imperativo (run_graph) executa execute_negotiation_domain_side_effects, persist_negotiation_domain_context e check_and_trigger_handoff após graph.invoke().

Por quê: side effects pós-resposta dependem fortemente de state_manager (objeto vivo). Movê-los para nós aumentaria acoplamento e tornaria rollback arriscado. Confirmado pelo Audit A — válido enquanto não houver checkpointer.

2026-05-07 · OBSERVABILIDADE

ADR-4: Spans Langfuse manuais (não LangfuseCallbackHandler)

Cada nó wrappa _emit_span com nomes idênticos aos do imperativo (perception, domain_agent, faq_lookup, planner). Zero regressão em test_langfuse_tracer.py.

Por quê: o callback handler oficial captura via abstrações LangChain (BaseChatModel), mas usamos anthropic SDK direto — não capturaria as LLM calls. Audit C confirmou que migrar perderia usage_details com tokens.

2026-05-08 · ECONÔMICA

Pacote 4 (Prompt caching Sonnet) marcado N/A

Audit B estimou ROI -71% custo Sonnet via cache_control. Após validação factual: _SYSTEM_PROMPT tem ~150 tokens, totalizando ~200 com _FIRST_TURN_BLOCK.

Por quê: Anthropic exige mínimo 1024 tokens para Sonnet caching. Premissa do audit estava errada. Reabrir só se prompt crescer organicamente. Aprendizado: audits subsequentes medem o tamanho real antes de estimar ROI.

2026-05-08 · BUG FIX

Try/except gigante em _create_indexes() escondia falhas reais

domain_audit_logs em prod tinha apenas o índice _id_. message_templates.create_index falhava com $ne in partialFilterExpression (não suportado por Mongo) e abortava silenciosamente todos os créates seguintes do mesmo bloco.

Por quê: o pattern try: a; b; c; d; except: log.debug(...) transforma erro em qualquer linha em "todos os índices subsequentes não existem". Fix corrigiu apenas o índice problemático; refactor pra try/except per-collection é V2.2 deferred (Pacote 6.1).

2026-05-08 · COMPLIANCE LGPD

TTL 5 anos em domain_audit_logs

expireAfterSeconds=157_680_000 na collection. Defesa em juízo de cobranças exige retenção mínima do registro de comunicação.

Por quê: collection crescia ilimitada — descoberto pelo audit (R-A1). Time-series collection migration (compactação bucket-based) deferida para V2.2 quando volume produtivo justificar.

2026-05-08 · INFRA

hnsw.ef_search=100 bloqueado em Supabase managed

Tentativas v1 (ALTER DATABASE) e v2 (SET em CREATE FUNCTION) falharam com ERROR 42501 — pgvector GUCs são superuser-only no Supabase managed.

Por quê: o role postgres do SQL Editor não é superuser real. Aceitar default ef_search=40 (recall ~85-90%) com volume atual baixo. Reabrir via iterative scan pgvector 0.8+ ou Supabase support quando volume justificar.

§ 05 · ESTADO

O que é verde · em obras · esperando · degradado

Snapshot do que cabe na cabeça antes de pegar a próxima task.

✓ Funcionando em prod
10+
  • Webhook Twilio → resposta em 3-5s p50
  • Pipeline NEGOTIATION cascata 3 camadas
  • FAQ retrieval Qdrant 1.15.5 (similarity 0.77)
  • Sonnet planner Claude 4.6 (avg 3.78s)
  • Multi-tenant Asaas (subaccount + pix_key)
  • Payment retry queue Redis ZSET + worker
  • CRM search_similar 0.70/0.55 adaptive
  • Audit_log MongoDB com TTL 5y LGPD
  • Sentry release attribution (RELEASE_SHA)
  • Frontend admin /AdminCRMMetrics
⟳ Em obras / pendente
4
  • 4 visualizações HTML (esta + 3 outras)
  • Smoke #2 Sentry · pendente confirmação visual painel
  • Trocar senha admin (resetada para debug)
  • .env-local pendente de remover
⏸ Bloqueado externamente
5
  • Fase 2 LangGraph · regra ≥5d → libera 2026-05-12
  • CHATWOOT_HANDOFF · conta Chatwoot Pablo
  • TWILIO_BR_NUMBER · +55 11 5028 7190 + Meta WABA
  • MULTI_TENANT_ASAAS smoke real · KYC PIX produtivo
  • Sentry painel · plano free 1 user · Pablo é owner
⚠ Degradado / dívida
3
  • HNSW recall ~85% (ef_search=40, blocked Supabase)
  • Qdrant warning client 1.17 / server 1.15 (2 minors skew)
  • handle_auth_flow refactor parcial (442→538 com inflate)
§ 06 · MENTAL MODEL

8 invariantes que precisam estar sempre na cabeça

Coisas que se violadas quebram o sistema de formas não-óbvias.

Feature flags em prod

MIGRATE_CONVERSATIONAL_AI=true (default) e MIGRATE_NEGOTIATION_TO_LANGGRAPH=false (default OFF). Mudança requer docker compose up -d --force-recreate app — restart simples não recarrega env_file.

scp manual quebra git pull

Hot-fix via scp + docker cp deixa working tree do servidor dirty. Próximo CI deploy falha com "Your local changes would be overwritten by merge". Reset hard no servidor antes de cada push subsequente.

$ne em partialFilterExpression

MongoDB whitelist: $eq, $exists, $gt/$lt, $type, $and, $or. $ne e $exists: false rejeitados. Try/except gigante esconde isso — validar índices via list_indexes() direto no driver.

Anthropic não tem embeddings

Embeddings são via Voyage AI ou OpenAI text-embedding-3-small. Path anthropic.embeddings.create com modelo retired claude-3-sonnet-20240229 existia e quebrava silenciosamente sob LLM_PROVIDER=anthropic — corrigido em b8156a5.

Pre-DEFINE checklist obrigatório

Antes de propor feature SDD nova, validar archives em .claude/sdd/archive/ e ler precedentes. Archives podem ser herdados de outras branches (_LANGGRAPH_MIGRATION_v1_NOT_APPLIED veio do V1 e nunca foi aplicado no V2).

Side effects fora do grafo

ADR-2 do LangGraph Fase 1: graph.invoke() não chama execute_negotiation_domain_side_effects nem persist_negotiation_domain_context. Quem chama é o caller run_graph após o invoke. Reabrir essa decisão se introduzir interrupt() ou checkpointer.

Pin de versões em requirements.txt

47 deps com == exato (49e9535). Bump intencional agora exige edição explícita. Eliminou risco de "bump cascata" descoberto durante CREWAI_TO_LANGGRAPH P1 (langchain 0.1→1.2, anthropic 0.25→0.100).

Subagents têm sandbox restrito

Subagents general-purpose não conseguem escrever em .claude/visuals/ (descoberto hoje gerando os HTMLs). Workaround: salvar em /tmp/ e mover via main agent. Main agent tem write OK (testado via touch).

§ 07 · DÉBITO COGNITIVO

Áreas onde o entendimento é mais frágil

Não é dívida técnica de código. É dívida de compreensão.

alta try/except gigantes em mongodb_provider._create_indexes()

Cada bloco try cobre múltiplas collections. Falha em uma cancela o resto silenciosamente (nível DEBUG). Já causou um bug crítico (índices nunca criados em prod). Pacote 6.1 do audit propunha refactor mas foi deferido.

Sugestão: quebrar em try/except per-collection e gritar via logger.error, não logger.debug. Adicionar smoke test que valida count de índices esperados no boot.

média /auth/me chamado em 15+ páginas

Dashboard, Pagamentos, Devedores, Layout, Negociações etc. cada um chama apiClient.getCurrentUser() no mount. Layout já chama; filhas chamam de novo. Cada navegação SPA dispara N requests redundantes.

Sugestão: centralizar em React Context populado uma vez no AuthProvider. Páginas filhas leem do context. V2.2.

média handle_auth_flow refactor parcial (442 → 538 LOC)

Decomposição em 1 dispatcher + 4 helpers funcional, mas total subiu 22% por inflate em docstrings/type hints. O design pretendia diminuir. Ainda é melhor que God function — mas não é "fim".

Sugestão: revisar próximas extrações com olho na concisão. Targets ≤200 LOC no dispatcher original. Extrair lógica não-auth (e.g. session_manager calls) pra helpers separados.

média Decisão de Fase 4 LangGraph (CHECKOUT+INGESTION) pendente

Tools com @tool CrewAI em utils/: chamar função direto (bypass CrewAI) ou redecorar como @langchain_core.tools.tool? Sem decisão, BUILD da Fase 4 estoura timebox.

Sugestão: spike de 2h pré-Fase 4 prototipando ambas opções num único tool. Documentar trade-off em mini-DEFINE. Audit A já marcou esse risco (R-Roadmap-2).

baixa Memory project_audit_2026_05_08.md ainda lista P4 como ROI alto

O memory inicial dizia "Pacote 4 prompt caching Sonnet -71% custo". Após validação, P4 foi marcado N/A no audit doc — mas o memory pode ter menção residual desatualizada.

Sugestão: revisar memory ao final da sessão, conferir consistência com audit doc. Ou adicionar bullet "P4 N/A — premissa errada".

baixa Re-estimativa de Fase 5 (1d → 2-3d) não está visível no DESIGN original

DESIGN_CREWAI_TO_LANGGRAPH.md está em archive/ e não foi atualizado. Quem ler o DESIGN sem passar pelo audit doc vai assumir 1 dia.

Sugestão: adicionar nota no topo do DESIGN apontando pra audit. Ou copiar a nota de re-estimativa pra um anexo do DESIGN.

§ 08 · PRÓXIMOS PASSOS

Para onde o momentum estava apontando

Não é prescrição — é o vetor da última semana de trabalho.

  1. 2026-05-12 · Iniciar Fase 2 LangGraph (AUTH). ~1-2d
    Estado AUTH é determinístico (template estático, zero LLM). Ideal pra solidificar pattern Fase 1 com risco mínimo. Pré-condição libera quando Fase 1 cumprir 5 dias estável em prod.
  2. Hoje · Smoke #2 Sentry visual. ~3min manual
    3 events disparados via SDK retornaram event_ids. Pablo (owner Sentry) precisa logar e confirmar release attribution 196d8aa... no painel. Plano free só permite 1 user.
  3. Hoje · Limpeza de credenciais. ~1min
    .env-local tem credencial de smoke (resetada). Apagar. Trocar senha do user admin no app pra uma só sua.
  4. Antes da Fase 4 · Spike de tool strategy. ~2h
    Decidir entre call-direct vs @langchain_core.tools.tool re-decoration. Prototipar num único tool. Documentar trade-off.
  5. Antes da Fase 5 · Plan refactor de domain_agent.process_turn. ~1d
    Substituição não-trivial — produz outcome dict completo (intention, next_stage, side_effects, payment_plan, handoff_flags). Não é pip uninstall crewai; é reescrever a lógica que sustenta todos os estados não-NEGOTIATION.
  6. Quando volume justificar · Reabrir HNSW recall. ?
    Iterative scan pgvector 0.8+ (sem GUC), Supabase support pra elevar role, ou self-hosted Postgres. Volume atual (6 interações) não justifica.
  7. V2.2 backlog · Async clients hot paths. ~2-3d
    AsyncMongoClient em webhook + audit_log; supabase-py async em crm_service. Blast radius 31 arquivos importam Mongo. Reabrir como SDD próprio quando webhook throughput virar gargalo medido.