Conteúdo Infinito — Historico de Bugs, Fixes e Decisoes¶
Atualizado: 2026-07-08 · por: sessão manual · aprovação em princípio da segmentação de clusters · rev: 6
Cronologia consolidada. Para arquitetura atual, ver
ARQUITETURA.md.
2026-04-14 — Lancamento e primeiros 5 fixes¶
Sessao: clever-amazing-ride
WF0 rodou em 13/04 (domingo), gerando 1000 atividades (500 email + 500 whatsapp). WF1 processou pela primeira vez em 14/04. Cinco problemas identificados e corrigidos no mesmo dia:
Fix 1: WF1 buscava atividades com datas futuras (CRITICO)¶
- Problema:
end_datehardcoded como"2026-04-30"trazia atividades de Apr 14 a Apr 28. Apenas 12 das 200 eram de hoje. - Fix:
start_date = 2020-01-01(pega atrasadas),end_date = $now.toFormat('yyyy-MM-dd')(exclui futuras) - Decisao Rafael: "quero tudo que tiver, no passado tambem, hoje e pro passado"
Fix 2: Make callback enviava body vazio ao WF3¶
- Problema: Modulo 99 (callback) nos cenarios Vendas1-6 usava
{{42.id}}para person_id. Modulo 42 e o parser JSON, nao o webhook — resolvia vazio em algumas execucoes. - Impacto: 34 atividades nao tiveram proxima agendada (leads sairam da cadencia)
- Fix inicial: Trocado para
{{43.person_id}}— corrigido novamente em 17/04 (ver abaixo)
Fix 3: WF1 processava tudo de uma vez (sem throttle)¶
- Problema: 160 leads em 64 minutos, sobrecarregava OpenRouter
- Fix: Schedule de
0 8,10,12,14,16para*/10 8-17, limit de 200 para 5
Fix 4: Parser de IA falhava com JSON invalido¶
- Problema: 1 falha matava o loop inteiro (39+ items sem processar)
- Fix:
onError: continueRegularOutput+retryOnFail: true, maxTries: 2em 3 chains de IA
Fix 5: OpenRouter timeout¶
- Problema: Timeout esporadico na IA Etapa 2
- Fix:
retryOnFail: true, maxTries: 3, waitBetweenTries: 5000em 4 nos OpenRouter
Fix WF0: Distribuicao de batch¶
- Antes: ambos canais com batch=10, distribuicao identica
- Depois: email batch ~24/dia (~1 mes util), whatsapp batch ~8/dia (~1 trimestre util)
2026-04-17 — Fix Make callback (5 sessoes, 3 context collapses)¶
Sessoes: multiple (4-5 sessoes consecutivas com context collapses)
Saga do modulo 99 Make¶
- Sessao 1: Fix aplicado (
{{42.id}}→{{43.person_id}}) em todos 6 cenarios. Rafael reportou que fix estava errado. - Sessao 2: Re-investigacao. Rafael confirmou: "person_id tem que vir do parser JSON". Re-fix:
{{43.person_id}}→{{42.id}}em todos 6. - Sessao 3: Descoberta critica — Make HTTP module tem DOIS campos (
bodyedata). UI renderizadata, API lebody. Fix anterior so setavabody→ UI mostrava vazio. Re-fix final: setar AMBOS em TODOS 7 cenarios (Principal + Vendas1-6). - Sessao 4: Verificacao dos 17 webhooks de remediacao — 17/17 passaram por todos 7 nodes do WF3. 17 leads remediados com sucesso.
- Sessao 5: Implementacao do WF2 "Sem Contato" e WF3 "Validacao Unsubscribe" (Fases 2 e 3)
WF2 — Tratamento de leads sem contato (Fase 2)¶
- 3 novos nodes: IF Tem Email, IF Tem Phone, Fecha Atividade Sem Contato
- Sem contato → marca done no Pipedrive SEM chamar WF3 (evita loops infinitos)
- Testado E2E com 3 execucoes (WF0 filtro + WF2 email sem contato + WF2 whatsapp sem contato)
WF3 — Validacao de unsubscribe (Fase 3)¶
- 2 novos nodes: Valida Aceita Comunicacao (Code) + IF Pode Continuar? (IF)
- Logica granular por canal (So Email + whatsapp = bloqueia, etc.)
- Testado E2E com 3 cenarios (aceita, nao aceita, canal incompativel)
2026-04-22 — Bug isNotEmpty nos IF nodes do WF2 (CRITICO)¶
Sessao: verificacao pos-deploy
Descoberta¶
Os IF nodes "Tem Email" e "Tem Phone" do WF2, criados via REST API PATCH em 17/04, usavam operador isNotEmpty. Esse operador NAO FUNCIONA em nodes criados via API no n8n v2.2 — sempre avaliava false.
Impacto¶
- TODAS as execucoes WF2 de nutricao entre 20/abr e 22/abr 19:33 UTC foram para "Fecha Atividade Sem Contato"
- 95 pessoas afetadas (72 email + 23 whatsapp)
- Atividades fechadas sem nova atividade = leads sairam da cadencia
Fix¶
isNotEmptytrocado pornotEqualscomrightValue: ""- Validado com 6 testes (3 diretos + 3 edge cases)
Remediacao¶
- Scan de 7.904 execucoes WF2 identificou 95 afetados
- 95 webhooks enviados ao WF3 (nutricao-cadencia)
- 86+ novas atividades criadas, 0 bloqueados, 0 erros
- Nota: primeira tentativa usou URL errada (
nutricao-gestao-cadencia→ 500), corrigido paranutricao-cadencia
Licao¶
NUNCA usar isNotEmpty em IF nodes criados via REST API do n8n. Sempre usar notEquals "".
2026-04-28 — Revisao do prompt de cadencia (v1 → v2)¶
Sessao: optimistic-dazzling-clarke Workflow afetado: muzljkMVH8vWhEXO (Personalizacao da Mensagem Cadencia)
Problema¶
Mensagens da IA eram longas (~50 palavras), tom de chatbot corporativo, perguntas sem objetivo de vendas claro, sem adaptacao para leads frios.
Mudancas¶
- Limite: 2 frases, 35 palavras maximo
- Removida estrutura obrigatoria de 3 passos
- Logica de "temperatura do lead" (morno/esfriando/frio baseado em msgs sem resposta)
- 6 tipos de pergunta → 4 tipos com nome de objetivo de vendas (Timeline, Decisor, Bloqueio, Proximo passo)
- Banidas frases de transicao/justificativa
Backups: backup_prompt_cadencia_v1_2026-04-28.md, prompt_cadencia_v2_proposta.md
2026-05-04/05 — Implementacao do Fila Mode (v1 → v2)¶
Arquivo de progresso: progress_control_nutricao_fila.md
Motivacao¶
Rafael pediu "modo fila" — WF1 busca tudo de uma vez mas processa 1 por vez, evitando sobrecarga de IA + Pipedrive + race conditions de duplicatas.
Implementacao¶
- Criada tabela
nutricao_filano PostgreSQL - WF1 reescrito: removido Loop Over Items, adicionados 8 nos de fila
- Schedule mudado para
*/5 8-17(cada 5 min) - V2: pick 3 por ciclo, skip Pipedrive se fila tem items, auto-pausa via staticData
- Total: 48 nos, 0 referencias quebradas
Bug SQL WHERE id = (subquery LIMIT 3) (05/05)¶
- Problema:
=espera escalar,LIMIT 3retorna multiplas rows → erro "more than one row returned" - Impacto: ~40 execucoes com erro durante o dia inteiro (18:55-20:55 UTC)
- Fix:
WHERE id IN (subquery LIMIT 3) - Licao: Postgres exige escalar com
=. UsarINquando LIMIT > 1.
Teste E2E validado (exec 408550, 05/05)¶
- Queue skip Pipedrive quando tem items
- Pick Batch atomico com FOR UPDATE SKIP LOCKED
- Prepara Item extrai activity_data JSONB corretamente
- Mark Done atualiza status=done + processed_at
2026-05-06 — Bug .first() e bug "Thiago"¶
Bug .first() no Verifica Filtros¶
- Problema:
$('Prepara Item da Fila').first().jsondentro do SplitInBatches sempre retornava item 0, nao o item da iteracao atual - Impacto: 42 items stuck em
processing(Mark Done atualizava item 0 tres vezes, items 1 e 2 ficavam orphaned) - Fix: Adicionado
pairedItem: {item: i}no Code "Prepara Item da Fila" + trocado.first()por.itemno "Verifica Filtros" - ERRO DE DIAGNOSTICO: Resetei 42 items de
processing→pendingANTES de investigar. Eles JA TINHAM SIDO PROCESSADOS (mensagens enviadas via WF2). Corrigido paradoneantes de reativar. - Licao: NUNCA resetar items na fila sem investigar se ja foram processados. Items em
processingpodem estar abandonados (bug no Mark Done) mas com mensagens JA ENVIADAS.
Bug "Thiago" (IA alucinava nome proprio)¶
- Problema: IA Etapa 2 inventava nome "Thiago da Bepex" nas mensagens WhatsApp
- Causa: Prompt nao proibia nome proprio
- Fix: Adicionada regra no system prompt: "NUNCA se apresente com nome proprio. NAO invente nomes como Thiago, Carlos, etc."
- Licao: Prompt de IA precisa PROIBIR explicitamente coisas que nao quer. Alucinacao de identidade e risco real.
2026-05-13 — Auditoria de saude (tudo OK)¶
- 280 execucoes WF1 nos ultimos dias, 0 erros
- WF1 auto-pausado (fila vazia, Pipedrive sem atividades pendentes)
- 4 leads filtrados corretamente pelo WF2 (canal=email sem email → Fecha Atividade Sem Contato)
- Sistema estavel e rodando conforme esperado
2026-07-03 — Bug critico no WF4 Opt-out Email: nenhum opt-out atualizava o Pipedrive desde a ativacao¶
Sessao: scheduled task bepex-fix-wf4-optout (autonoma, sem Rafael presente)
Workflow afetado: paffbqyOJHAafePk (WF4 — Opt-out Email)
Descoberta¶
O no IF "Ja Optou Out?" checava um campo nutricao_unsubscribed que nao existe no schema de Pessoa do Pipedrive — resquicio de uma versao anterior nunca finalizada (nao e aceita_comunicacao nem nenhum custom field real). Como o campo era sempre undefined, a condicao sempre roteava para o branch falso ("Respond Ja Optou Out"), e os nos "Calcula Opt-out" e "Atualiza Pessoa" nunca executaram desde a ativacao do workflow em 2026-04-07.
Impacto¶
Confirmado via list_executions/get_execution_node_output: as 4 unicas execucoes reais do WF4 ate 2026-07-03 (617086, 613550, 613549, 606527) terminaram todas em "Respond Ja Optou Out" sem passar por "Atualiza Pessoa". Nenhum clique em link de descadastro, em 3 meses, atualizou o Pipedrive — o lead recebia a mensagem de sucesso mas continuava recebendo nutricao.
Fix (v1, incompleto — ver correcao abaixo)¶
- No IF "Ja Optou Out?": condicao trocada de
$json.data?.nutricao_unsubscribed(campo inexistente) para checar o campo realaceita_comunicacao(19bc9c4cde5a4a5959809e45e7f032ae7dd9c3a7) —notEquals '397'como branch verdadeiro (segue pro opt-out),== '397'como falso (ja optou out). Esta parte do fix ficou correta e nao foi alterada depois. - No Code "Calcula Opt-out" (v1, ERRADO): a descricao da tarefa agendada instruia logica simplificada — qualquer clique marca a pessoa DIRETO como
397(Nao aceita comunicacao). O Rafael corrigiu isso na hora: WF4 e opt-out de EMAIL apenas, nao opt-out total. Ver secao "Correcao da logica" abaixo. - Aplicado via PATCH REST (
browser-idheader) + ciclo deactivate→activate com versionId fresh. Backup do workflow pre-fix salvo emoutputs/WF4_backup_pre_fix_2026-07-03.json(fora deste repo, no scratchpad da sessao).
Correcao da logica (mesma sessao, poucos minutos depois)¶
Rafael apontou o erro conceitual: WF4 e "Opt-out Email", nao opt-out geral. A regra correta e nuancada, igual a logica original do Code node (que nunca tinha rodado em producao, mas estava certa):
null(nunca definiu) ou396(Email e WhatsApp) → tira o email, mantem WhatsApp → 395 (So WhatsApp)394(So Email) → essa era a UNICA via aberta; tirando o email, fecha tudo → 397 (Nao aceita comunicacao)395(So WhatsApp) → ja nao recebe email, nada a fazer (sem mudanca)
Corrigido no Code "Calcula Opt-out" e revalidado com os dois casos reais via pessoa_id 1953 (ver Teste E2E).
Remediacao (3 pessoas afetadas)¶
Passo 1 (execucao com a logica v1 errada) setou as 3 pessoas para 397. Passo 2 (apos a correcao) reverteu para o valor certo:
| pessoa_id | Nome / Empresa | aceita_comunicacao original |
setado errado (v1) | valor final correto | Observacao |
|---|---|---|---|---|---|
| 11792 | RAFAEL, RDP Solucoes Energeticas | 396 (Email e WhatsApp) | 397 | 395 (So WhatsApp) | Caso real — estava EM CADENCIA ATIVA quando clicou em 2026-06-30 |
| 15375 | Ailton Serafim, Aramificio Nordeste | null | 397 | 395 (So WhatsApp) | Clicou 2026-07-03 11:32 (provavel teste do Rafael) |
| 15612 | Rafael, Kopp Construcoes | null | 397 | 395 (So WhatsApp) | Clicou 2x em 2026-07-02 |
Todas as 3 confirmadas via GET fresh (ainda em 397, nada mais tocou o campo) e depois via output do PUT de correcao (19bc9c4cde5a4a5959809e45e7f032ae7dd9c3a7 = "395", update_time = 2026-07-04 01:25).
Teste E2E¶
- Logica v1 (errada): disparo com
pessoa_id=1953(estava em 396) → virou 397. Segundo disparo → idempotente (Respond Ja Optou Out). Restaurado para 396. - Logica corrigida — caso 396→395: disparo com
pessoa_id=1953(396) → "Calcula Opt-out" retornouvalor_anterior: "396", novo_valor: 395. Confirmado correto. - Logica corrigida — caso 394→397: pessoa 1953 setada temporariamente para 394 via probe, disparo do webhook → "Calcula Opt-out" retornou
valor_anterior: "394", novo_valor: 397. Confirmado correto. - Cleanup: pessoa 1953 restaurada para
396(valor original) em ambos os ciclos de teste, via workflow de manutencao_TEMP_Probe_Pipedrive(toquIjzNhIsNN59O), que foi devolvido ao estado anterior (query DELETE arquitetos) apos cada uso.
Licao¶
Nao existe execucao verde suficiente. As 4 execucoes do WF4 sempre mostraram status: success — o bug so aparece inspecionando o output do no "Atualiza Pessoa" (nunca rodou) node-a-node. Um IF node que aponta pra um campo que nunca existiu passa despercebido indefinidamente se ninguem verificar o efeito colateral real (o PUT no Pipedrive).
Nome do workflow e a fonte da verdade sobre escopo. "WF4 — Opt-out Email" significa opt-out de EMAIL, nao de tudo. Uma instrucao de tarefa agendada que diz "logica simples, sem nuance" deve ser confrontada com o nome/proposito do workflow antes de implementar — nao seguida ao pe da letra sem question. Nuance existia por um motivo (granularidade de canal), nao era acidente.
2026-07-03 — Segmentação da base em 6 clusters nativos (RFM abandonado) + modelo de cadência alternada¶
Sessão: sessão manual · segmentação de clusters e cadência de nutrição/reativação
Contexto¶
Reunião de 2026-07-02 com Enrico Bertanha e Gustavo Sander pediu clusterizar a base Pipedrive por RFM pra definir cadência de nutrição (conteúdo) e reativação (comercial) por canal, e dimensionar quantos números de WhatsApp seriam necessários.
Investigação do RFM nativo¶
O Pipedrive tem campos RFM Segment/RFM Score (Pessoa e Organização), criados em 2024-08-21. Cobrem só 805 de 59.617 pessoas (2,3% da base) e nunca foram recalculados desde a criação — sem automação de atualização, só um workflow legado (5OjAbflpRHfEMrJu, "Relatório diário pipedrive", hoje inativo) que os lia. Decisão do Rafael: abandonar RFM por enquanto — não vale a pena manter/automatizar um campo que cobre 2,3% da base. Usar modelo simples com contagens nativas de negócios.
Bug real descoberto durante a investigação (documentado na entrada acima, mesma data)¶
Zero pessoas na base estavam marcadas aceita_comunicacao = 397 (Não aceita), apesar do Rafael ter testado o fluxo de opt-out pessoalmente — o que levou à descoberta do bug do WF4 (IF checando campo inexistente nutricao_unsubscribed), corrigido e remediado separadamente via scheduled task bepex-fix-wf4-optout.
Modelo final de segmentação — 6 clusters, campos nativos¶
Baseado em won_deals_count (9050), open_deals_count (9045), lost_deals_count (9051), closed_deals_count (9052), last_activity_date (9048, corte de 2 anos), excluindo Junk (9087 IS NULL) e opt-out (9094 != 397). Cada cluster tem filtro real e clicável no Pipedrive — decisão explícita do Rafael: "não dá pra mandar sem eu poder conferir o filtro de verdade, eu quero o link do filtro pra eu ir lá verificar."
Iteração v1 → v2 (pedido do Rafael, após ver a v1): clusters "nunca abriu negócio" e "já orçou nunca fechou" foram fundidos (não fazia sentido separar — o critério real é só won_deals_count=0); criado cluster novo "Cliente recorrente" (fechou 1x mas orçou mais de 1 vez — tem outro negócio aberto ou perdido) entre os antigos clusters 3 e 4; "Cliente novo" renomeado para "Cliente ativo". Contagens finais e IDs de filtro em REFERENCIA_TECNICA.md.
Pegadinha da API de Filtros do Pipedrive¶
Ao criar os 6 filtros via POST /v1/filters, descoberto que o array conditions.conditions no nível raiz aceita no máximo 2 itens, e um grupo aninhado não pode conter outro grupo aninhado (só condições-folha). Erros enfrentados: "must NOT have more than 2 items" e "must have required property 'object'". Padrão que resolveu: agrupar todos os AND num único grupo-folha e todos os OR noutro, combinando os dois no nível raiz (exatamente 2 itens). Detalhes e exemplo de payload em REFERENCIA_TECNICA.md.
Também descoberto no meio do processo: o token da API do Pipedrive (window.__pdToken, capturado via /settings/api) expirou/rotacionou durante a sessão — todas as chamadas de criação de filtro falharam com 401 até recapturar o token na página de settings.
Modelo de cadência — erro de design apontado pelo Rafael e corrigido¶
Primeira versão do modelo de cadência definia frequências independentes de nutrição e reativação no mesmo canal (ex.: Cluster 3 WhatsApp nutrição a cada 45 dias E reativação a cada 21 dias). Rafael identificou o problema: dois relógios independentes no mesmo canal colidem — quando os ciclos coincidem, a pessoa recebe duas mensagens no mesmo dia, e a frequência de contato real fica imprevisível (ele estimou de cabeça que ficaria "13 em 13 dias" combinando dois relógios de 21 dias, ilustrando a colisão).
Fix: modelo de calendário único por canal por cluster. Uma frequência só de contato; o tipo alterna automaticamente a cada toque (nutrição, reativação, nutrição...). Elimina colisão por design — cada tipo específico ocorre a cada 2× a frequência de contato, sem soma nem coincidência possível. Cluster 1 (Frio) é exceção: só nutrição, sem alternância (nunca fechou negócio, nada a reativar).
Dimensionamento de WhatsApp¶
Modelo final soma ~256 msgs/dia (Cluster 1 Frio sozinho = ~172/dia, 89% da base). Recomendação: 10 números (30 msgs/dia cada = 300/dia, ~15% de folga sobre o volume calculado). Hoje existem 6 números (Vendas1-6) — faltam 4.
Entrega¶
Proposta consolidada em HTML tema Bepex (Proposta_Clusters_Cadencia_WhatsApp_2026-07-03.html, nesta pasta) — 3 iterações de conteúdo até o Rafael aprovar (v1: cadência com relógios separados; v2: cadência corrigida com calendário único). Enviada ao Enrico Bertanha via WhatsApp Bepex (Chatwoot conta 3, inbox 26, conversa 56) em 2026-07-03: texto via MCP enviar_mensagem (mensagem 2701129) + anexo HTML via upload na UI do Chatwoot (mensagem 2701134, status: sent, confirmado via buscar_mensagens). Status: aguardando decisão/aprovação do Enrico — proposta NÃO implementada no WF3.
Lição¶
Segmentação sofisticada (RFM ou qualquer outra) não vale a pena se a base de cálculo não é auditável em tempo real pelo dono do negócio — entregar sempre com filtro real e clicável, não número calculado à parte. Também: cadência com dois relógios independentes no mesmo canal é um anti-pattern — sempre modelar como um calendário único com tipo alternado quando há mais de um tipo de mensagem no mesmo canal.
2026-07-03/04 — Guard de idempotência no WF3 (nunca mais criar atividade duplicada)¶
Sessão: scheduled task wf3-fix-anti-duplicacao (autônoma, sem Rafael presente)
Workflow afetado: ny4gXcNU3dReDCba (WF3 — Gestão de Cadência)
Bug original¶
"Cria Próxima Atividade" criava incondicionalmente, sem checar se já existia uma atividade aberta do mesmo tipo pra mesma pessoa. Como o WF3 é chamado por múltiplos caminhos que podem disparar em duplicidade (WF2 email, Make Vendas1-6 + retries WhatsApp, WF1 pool esgotado, retries de webhook), o mesmo envio podia gerar 2+ atividades idênticas. Evidência original: pessoa 269 com 3 atividades de email idênticas abertas (venc. 2026-06-01).
Fix — guard inserido antes de "Cria Próxima Atividade"¶
Backup pré-fix: backup_wf3_pre_idempotencia_2026-07-06.json (nesta pasta). 3 nós novos inseridos entre "E Feriado?" (branch falso) e "Cria Próxima Atividade":
- Verifica Atividade Aberta (HTTP GET) —
https://api.pipedrive.com/v1/persons/{person_id}/activities?done=0&limit=100, credencialBepex Enrico(leitura) - Avalia Atividade Aberta (Code) — filtra a lista retornada por
type === activity_type && done === false, produzja_existe_atividade_aberta(bool) - Já Existe Atividade Aberta? (IF, bool true/false) → true: "Guard - Pula Criação (Já Existe)" (NoOp, dead-end) · false: segue pro "Cria Próxima Atividade" original (inalterado — continua referenciando
$('Calcula Próxima Data').item.json.*, não$json, então não precisou de nenhuma mudança)
O guard vale pra qualquer caminho que chegue no webhook nutricao-cadencia, incluindo o disparo do WF1 com pool esgotado — não há bifurcação de lógica por origem da chamada.
Bug descoberto DURANTE o teste (quase virou regressão pior que o original)¶
A primeira versão do guard usava GET /v1/activities?person_id=X&type=Y&done=0 (endpoint de coleção). /v1/activities do Pipedrive v1 NÃO aceita person_id como filtro de query string — o parâmetro é silenciosamente ignorado. Validado na execução 621835: a query com person_id=1953 retornou uma atividade de uma pessoa completamente diferente (person_id 12857, "Mario"/Movecargas). Como o array vinha não-vazio, o guard bloqueava a criação incondicionalmente para qualquer pessoa, inclusive na primeira chamada legítima — ou seja, o fix quebrado teria parado 100% da cadência do WF3 se não tivesse sido pego no teste E2E antes de confirmar como "pronto".
Fix do fix: trocado pro endpoint correto, escopado por pessoa: GET /v1/persons/{person_id}/activities?done=0&limit=100, com o match de type feito em Code node (não em query string, já que esse endpoint não parece filtrar por type de forma confiável — testado só com done=0).
Teste E2E (execuções 621852 e 621861, pessoa 1953)¶
Descoberta na validação: pessoa 1953 já tinha 3 atividades abertas (2× nutricao___email idênticas — devido 2026-06-17 e 2026-06-22 — + 1× nutricao___whatsapp), um caso real do mesmo bug de duplicação que o guard corrige, pré-existente à sessão. Isso invalidou o teste "limpo" planejado (criar 1ª chamada, bloquear 2ª) porque o canal email já estava ocupado — qualquer chamada nova seria (corretamente) bloqueada desde a primeira. Adaptação: em vez de fabricar um cenário sintético tocando outra pessoa real do Pipedrive (fora do escopo autorizado), validei o guard contra o estado real "sujo":
- Disparo 1 (canal=email, ativity_id fake
999999999): guard viu 3 atividades abertas, 2 do tipo alvo →ja_existe_atividade_aberta: true→ bloqueou, "Cria Próxima Atividade" nunca executou - Disparo 2 (mesmo payload, poucos segundos depois): idêntico — bloqueou de novo,
count_abertas_pessoa: 3sem crescer Marca Atividade Donefalhou graciosamente no ID fake (onError: continueRegularOutput, "resource could not be found") — nenhum efeito colateral real- Nenhuma atividade nova foi criada em nenhuma das duas chamadas — a contagem de atividades abertas da pessoa 1953 permaneceu 3 antes e depois. Sem artefato de teste pra limpar.
Não foi possível demonstrar o caminho "cria na 1ª, bloqueia na 2ª" isoladamente sem tocar em dados de outra pessoa real do Pipedrive fora do escopo do teste (ambos os canais de 1953 já estavam ocupados por atividades reais pré-existentes). Reportado ao Rafael como limitação do teste, não do fix — a lógica do guard (Code "Avalia Atividade Aberta") é determinística e foi inspecionada nó-a-nó nas duas execuções.
Achado colateral pra decisão do Rafael¶
Pessoa 1953 (Matias, Fita Metal Indústria) tem 2 atividades de email abertas idênticas (419958 due 2026-06-17, 421569 due 2026-06-22) — instância real do bug original, não criada por este teste. Não mexi nelas (não foram criadas pelo teste, fora do escopo de cleanup autorizado). Rafael pode querer marcar uma como done manualmente ou rodar uma limpeza dedicada como a feita em 2026-04-15 (ver skill n8n-bepex-conteudo-infinito, seção Manutenção) pra achar outras pessoas na mesma situação.
Lição¶
Testar o fix ANTES de confiar nele pode revelar que o fix é pior que o bug. O guard quebrado (filtro por person_id ignorado silenciosamente pela API) teria bloqueado a cadência inteira em produção — só foi pego porque o teste E2E inspecionou o payload real da resposta HTTP em vez de confiar no "success" da execução. Reforça o princípio geral: filtros de API precisam ser validados via output real, nunca assumidos a partir de padrões usados em endpoints diferentes (o padrão ?person_id=X funciona pra alguns endpoints do Pipedrive mas não pra /v1/activities; o endpoint correto pra atividades de uma pessoa específica é /v1/persons/{id}/activities).
Fechamento de 2 gaps da metodologia (mesmo dia, sessão seguinte)¶
Auditoria contra o checklist da skill n8n (Parte 4) revelou 2 furos no fix acima, fechados nesta sessão:
- Code "Avalia Atividade Aberta" sem assertion (Princípio 5 — "faça o workflow gritar"). Se
Verifica Atividade Abertaretornasse algo fora do formato{success, data}(erro de API, onError continueRegularOutput vira{error: msg}), o Code antigo fazia fallback silencioso pra lista vazia →ja_existe_atividade_aberta: false→ criava a atividade sem ninguém saber que o guard tinha falhado. Fix: assertion explícita (typeof resp.success === 'undefined') que marcaguard_error: true+guard_error_msgno output, mas decide fail-OPEN (segue e cria a atividade mesmo com erro no guard) — decisão consciente, não a mesma coisa que a falha silenciosa do WF4. Motivo: a lição do WF4 (2026-07-03, mesma pasta) é que fail-closed silencioso é pior — lead cai da cadência sem ninguém perceber, escondido atrás destatus: success, por meses. Prefere-se o risco raro de 1 duplicata (visível e fácil de limpar) ao risco sistêmico e silencioso de leads saindo da nutrição sem aviso. - IF "Já Existe Atividade Aberta?" sem
typeValidationexplícito (Princípio 7). AdicionadotypeValidation: 'loose'nas options — antes seguia o padrão do "IF Pode Continuar?" (já validado em produção nesse workflow, mas sem a opção setada). Sem impacto observado no comportamento (a comparação já era boolean-para-boolean), mas fecha a recomendação da skill.
Reteste completo pós-fix (execução 621925, pessoa 1953, canal=email):
- Nível 1 (probe do filtro) — reconfirmado: /v1/persons/1953/activities?done=0 seguiu retornando só atividades da pessoa 1953 (count_abertas_pessoa: 3), igual à validação anterior.
- Nível 2 (nó-a-nó) — Avalia Atividade Aberta retornou guard_error: false no caminho normal (assertion não dispara em resposta bem-formada); ja_existe_atividade_aberta: true propagou corretamente até o IF e o NoOp.
- Nível 3 (E2E) — guard bloqueou a criação de novo (Cria Próxima Atividade não executou), count_abertas_pessoa continua em 3 — nenhuma atividade nova.
- Caminho de erro do guard (guard_error: true) NÃO foi disparado ao vivo — não dá pra simular um erro de API real em produção sem quebrar o fluxo de propósito. Validado por analogia: o nó "Marca Atividade Done" (mesmo tipo de node, mesmo onError: continueRegularOutput) errou de verdade nesta mesma execução (activity_id fake) e produziu exatamente o formato {error: msg} sem campo success que a assertion espera detectar — mesma forma de erro que Verifica Atividade Aberta produziria num 404/timeout real. Not fired diretamente, mas evidência forte (não suposição) de que o branch dispara certo.
2026-07-06 — Diagnóstico do backlog "congelado" e limpeza das atividades abertas¶
Sessão: sessão manual · "por que a nutrição parou" + delete das abertas
Sintoma¶
~1.384 atividades de nutrição abertas e atrasadas (venc. 05/05–01/06), paradas: sem enviar e sem recriar cadência. WF1 auto-pausado todo dia (fila vazia), WF2/WF3 sem disparos. Parecia "pipeline quebrado".
Causa raiz (a prova está na fila)¶
Amostra de 10 atividades abertas travadas → SELECT na nutricao_fila: todas status='done', error_message=null, e processed_at IDÊNTICO = 2026-05-06 17:40:11.714. Dez itens marcados done no mesmo milissegundo não é o pipeline processando (que faz 3/ciclo, espaçado) — é um UPDATE nutricao_fila SET status='done' em massa, manual, rodado em 06/05 (a remediação do bug .first() — ver entrada 2026-05-06). Esse UPDATE pegou demais: marcou como done leads que nunca foram enviados (nutricao_historico_ids vazio confirmado em 94/94 pessoas amostradas via API Pipedrive). Efeito: a atividade fica aberta no Pipedrive, a fila diz "done" → o WF1 nunca mais toca nelas (Pick Batch só pega pending). Órfãs permanentes. Não foi o WF3, não foi pool esgotado, não foi bug de código — foi uma limpeza manual da fila que se estendeu além dos itens realmente processados.
Exoneração do WF3 e do "pool esgotado"¶
- Scan das 3.091 atividades de nutrição (bucketizadas por padrão de assunto — WF0 cria
Nutrição - Email; WF3 criaNutricao Email - {nome}): o WF3 criou ~1.827 atividades de próxima cadência (967 email + 860 whatsapp) e fechou ~1.600. O WF3 funciona e sempre funcionou — as travadas nunca chegaram nele porque nunca foram enviadas. - Reprocesso ao vivo de 3 leads travados (reset fila
done→pending, clear da auto-pausa via staticDataglobal.pause_until, run manual do WF1):PostgreSQL - Filtra Conteúdosretornou 15 conteúdos (pool CHEIO — historico vazio, nada a excluir → NÃO é pool esgotado), a IA escolheu/escreveu,Envia para WF2disparou, e o WF2 → SES retornouMessageId(execução 621876). Os 3 e-mails saíram de verdade. O pipeline atual está 100% saudável — quem for reprocessado envia normalmente.
7 leads presos em processing (segunda 06/07)¶
Dos 10 preparados no reprocesso, 3 foram enviados sexta (done); os outros 7 ficaram pending e no fim de semana/segunda o WF1 os pegou mas travou em status='processing' sem completar (WF2 não disparou). Mais um estado órfão da fila (item claimed, nunca marcado done). Não investigado a fundo — a base ia ser apagada de qualquer forma.
Ação: limpeza das abertas (autorizada pelo Rafael, escopo "só abertas")¶
Apagadas 1.433 atividades de nutrição ABERTAS (633 email + 800 whatsapp) via bulk delete do Pipedrive (DELETE /v1/activities?ids=..., lotes de 100), 0 falhas. As 1.658 concluídas foram mantidas (histórico de contato real preservado). Delete é soft (recuperável, active_flag=false). Backup de todos os IDs: backup_atividades_abertas_deletadas_2026-07-06.txt (nesta pasta). Verificação pós-delete: 0 abertas, 1.658 concluídas. Objetivo: slate limpo para reprocessar toda a base com a nova estratégia de cadência clusterizada (ver entrada 2026-07-03, clusters). O nutricao_historico_ids (memória de não-repetir) mora na Pessoa, não nas atividades — não se perde nada com o delete. Pendente: definição final da frequência por cluster (Rafael) → montar a nova ignição; decidir se zera a nutricao_fila junto.
Lição¶
O backlog órfão foi causado por um UPDATE em massa manual na fila (06/05), não pelo código. Reforça a lição #7: nunca mexer em nutricao_fila em lote sem escopo cirúrgico e sem provar item-a-item se já foi processado/enviado — um WHERE status='processing' largo demais varre itens nunca enviados junto e os enterra como "done" para sempre.
2026-07-08 — Segmentação de clusters aprovada em princípio pelo Enrico¶
Sessão: sessão manual · aprovação em princípio da segmentação de clusters
A proposta de segmentação em 6 clusters + cadência alternada por canal (entregue ao Enrico Bertanha em 2026-07-03, ver entrada acima) foi aprovada em princípio pelo Rafael/Enrico. Nenhuma mudança de conteúdo na proposta em si — só atualização de status.
Ainda não implementada no WF3. Rafael vai abrir uma sessão dedicada para planejar a infraestrutura necessária (não é continuação direta desta sessão) — cobrindo, no mínimo, os 3 pontos já levantados em ARQUITETURA.md (campo de cluster por pessoa, campo de "último tipo de toque" pra alternância nutrição/reativação, e a nova tabela de frequência por cluster substituindo frequencia_email/frequencia_whatsapp mensal/bimestral/semestral). Essa sessão futura também precisa decidir se aproveita o slate limpo da limpeza de 2026-07-06 (0 atividades abertas hoje) para já nascer a nova ignição já no modelo de clusters, em vez de recriar a fila no modelo antigo e migrar depois.
Status atualizado em README.md e ARQUITETURA.md (seção "Segmentação de clusters e cadência").
Licoes consolidadas¶
- n8n: NUNCA bater em
/rest/loginou/rest/me— invalida sessao do Rafael - n8n: PATCH reseta
active=false— sempre reativar via/activatecom versionId fresh - n8n: Retry de execucao usa SNAPSHOT ANTIGO, nao a versao atual. Re-disparar via webhook.
- n8n:
isNotEmptyem IF nodes criados via API nao funciona. UsarnotEquals "". - n8n:
$('NoAntesDo Loop').first()dentro de SplitInBatches retorna item 0, nao o atual. Usar.itemcom paired-item. - n8n: Code nodes com
.map()SEMpairedItem: {item: i}quebram paired-item tracking. - n8n: NUNCA resetar items na fila sem investigar se ja foram processados.
- Make: HTTP module tem DOIS campos (
bodyedata). Setar AMBOS. - Make: Antes de alterar
{{N.campo}}, inspecionar ultima execucao real em History. - SES: Usar contentType raw + encodeURIComponent manual. Node nativo double-encodes
@. - IA: Prompt precisa PROIBIR explicitamente o que nao quer. Alucinacao de identidade e risco real.
- SQL:
WHERE id = (subquery LIMIT N)com N>1 quebra. UsarIN. - Postgres: Auto-pausa soft pode deixar items em
processingse pausa no meio do batch. - WF4: IF node que checa campo inexistente (
nutricao_unsubscribed) roteia sempre pro mesmo branch e passa despercebido comstatus: successpor meses. Sempre inspecionar o output do no de escrita (nao so o status da execucao) ao auditar workflows de opt-out/unsubscribe. - WF4 (opt-out por canal): "Opt-out Email" != "opt-out total". Quando um workflow tem escopo de canal no proprio nome, a logica de downgrade deve ser nuancada por canal (396/null→395 So WhatsApp, 394→397 total). Nao aplicar "reset total" so porque parece mais simples — verificar o proposito do workflow primeiro.
- Pipedrive Filters API:
conditions.conditionsno nivel raiz aceita no maximo 2 itens; grupo aninhado nao pode conter outro grupo aninhado (so leaves comobject). Pra logica mista AND/OR, agrupar todos os AND num grupo-folha e todos os OR noutro, combinando os dois no nivel raiz. - Segmentacao: RFM ou qualquer clusterizacao sofisticada nao vale a pena se a base de calculo nao e auditavel em tempo real pelo dono do negocio — sempre entregar com filtro real e clicavel, nunca so um numero calculado a parte.
- Cadencia multi-canal: dois relogios independentes (nutricao + reativacao) no mesmo canal colidem e geram frequencia real imprevisivel. Modelar como calendario unico por canal com tipo alternado (nutricao, reativacao, nutricao...) elimina o problema por design.
- Pipedrive API:
GET /v1/activitiesNAO filtra porperson_idna query string (ignorado silenciosamente). Pra atividades de uma pessoa especifica, usarGET /v1/persons/{id}/activities. Filtro quebrado quase virou regressao pior que o bug original (bloqueava TODA criacao de atividade, nao so duplicatas) — pego a tempo pelo teste E2E inspecionando o payload real, nao o status da execucao. - Backlog de atividades "abertas no Pipedrive + fila=done/processing" = fila mexida em massa, NAO bug de pipeline. Antes de culpar WF1/WF2/WF3 por leads travados, checar a
nutricao_fila:processed_atidentico em lote denuncia um UPDATE manual. Diagnosticar pela fila +nutricao_historico_ids(vazio = nunca enviado), nunca pela aparencia das execucoes. Diagnostico validado por reprocesso ao vivo (envio real via SES) — a forma definitiva de provar que o pipeline atual esta saudavel.