Files
b2bcall/docs/TROUBLESHOOTING.md
B2BCall Bootstrap 80e72881b2 feat: Fase 9/10 — métricas, scripts operacionais, callback/wrap-up e aceite final
Fase 9 (segurança e produção):
- GET /api/metrics: endpoint Prometheus com métricas reais (chamadas,
  agentes, filas, CPS por campanha), protegido por permissão
- scripts/backup.sh, restore.sh, healthcheck.sh, install.sh, update.sh —
  testados contra o ambiente real (backup.sh e healthcheck.sh rodados de
  verdade; install.sh/update.sh validados por inspeção, ambiente atual já
  provisionado)
- POST /api/agent-console/dispose: aplica disposição de chamada de verdade
  (lacuna deixada aberta desde a Fase 6), com ações CALLBACK (agenda
  retorno) e DO_NOT_CALL (suprime automaticamente)
- apps/dialer-worker/src/callback-sweep.ts: reativa leads com callback
  vencido; GET /api/callbacks para consulta
- apps/dialer-worker/src/wrap-up-sweep.ts: transição automática
  WRAP_UP -> AVAILABLE + despausa real na fila do Asterisk. Exigiu corrigir
  main.ts para conectar ao AMI mesmo em DIALER_SIMULATION=true
  (DIALER_SIMULATION deve impedir só originação de chamada, não ações
  administrativas de fila)
- nftables revisado (sem alterações necessárias)
- Documentação completa: INSTALL, OPERATIONS, BACKUP_RESTORE, SECURITY,
  DATABASE, API, ASTERISK, OPENSIPS (não implementado, motivo
  documentado), TROUBLESHOOTING
- README.md e CHANGELOG.md reescritos

Fase 10 (testes e aceite):
- Quality gate completo executado: build/typecheck (7 workspaces), lint,
  46 testes unitários, docker compose config/ps, healthcheck — tudo verde
- Aceite de segurança (seção 92): 13 itens verificados ao vivo contra o
  sistema real, não só por inspeção de código
- Aceite Asterisk (seção 93): os 5 comandos executados e documentados,
  comunicação API->AMI->Asterisk validada
- docs/RELATORIO_FINAL.md: relatório final no formato da seção 96

Todos os fixtures de teste desta fase foram removidos/desativados ao
final. Credenciais de acesso entregues separadamente em CREDENCIAIS.txt
(fora do git, nunca versionado).
2026-08-27 19:16:14 -03:00

4.6 KiB

B2BCall — Troubleshooting

Problemas reais encontrados durante o desenvolvimento deste projeto e como foram diagnosticados/resolvidos — mantido como referência para o futuro.

"Argument calledNumber is missing" ao originar chamada

Sintoma: PrismaClientValidationError ao reservar um lead, mesmo a query SQL parecendo correta.

Causa: $queryRaw do Prisma não aplica o mapeamento camelCase automático que findMany/update aplicam — colunas retornadas por SQL cru mantêm o nome exato da coluna (phone_normalized, não phoneNormalized).

Fix: sempre usar alias explícito no SQL: phone_normalized AS "phoneNormalized". Ver apps/dialer-worker/src/lead-repository.ts.

Pacing do discador oscilando/nunca parando de subir mesmo sem atividade

Sintoma: no cenário de teste da seção 66 (20 agentes/10 CPS/30% taxa de atendimento), o pacingFactor continuava subindo mesmo em períodos ociosos, gerando concorrência muito acima do esperado.

Causas (três, compostas):

  1. O cálculo de concorrência total não incluía chamadas já conectadas ao agente (agentConnectedCalls), subestimando quanto já estava "em uso".
  2. O ajuste de pacing subia mesmo sem nenhuma atividade de discagem no ciclo (nada "deu errado", então ele interpretava como "pode subir mais").
  3. Uma única iteração fechava 100% da diferença entre o alvo e o atual, causando overshoot.

Fix: agentConnectedCalls entra no total de concorrência; pacing nunca sobe durante ociosidade (hasActivity gate); fechamento de gap limitado a 50% por ciclo (RAMP_FRACTION); e dampening por raiz quadrada do próprio pacingFactor. Ver apps/dialer-worker/src/predictive-engine.ts e docs/PREDICTIVE_DIALER.md.

AMI: comando Command não retorna o output esperado

Sintoma: parsing manual do protocolo AMI para a action Command (usada por diagnóstico) não batia com a documentação legada (Response: Follows / --END COMMAND--).

Causa: o Asterisk 22 usa um formato diferente para Command: múltiplos headers Output: repetidos, um por linha de saída — não o bloco Follows/END COMMAND de versões antigas.

Fix: reverse-engineering via socket TCP cru (script de debug dedicado) para confirmar o formato real antes de implementar o parser em packages/telephony.

Erro de sintaxe no schema.prisma: @default(1_000_000)

Sintoma: prisma generate falha com erro de parsing.

Causa: separador de milhar com underscore (1_000_000) não é suportado na sintaxe do .prisma — é um recurso de JS/TS, não do DSL do Prisma.

Fix: escrever o número sem separadores: 1000000.

Prisma.InputJsonValue rejeita instância de DTO

Sintoma: Index signature for type 'string' is missing ao passar um objeto DTO diretamente para um campo Json do Prisma (ex.: AuditLog.after).

Causa: uma instância de classe (mesmo com os campos certos) não satisfaz a assinatura de índice que InputJsonValue exige — só um objeto literal simples satisfaz.

Fix: espalhar em um objeto literal: after: { ...dto } em vez de after: dto.

Redis: agentes travados em IN_CALL para sempre

Sintoma: depois de uma chamada, o agente nunca voltava a AVAILABLE.

Causa: o "claim" de agente disponível só marcava IN_CALL na conexão — não havia nenhum mecanismo simétrico de liberação após o fim da chamada.

Fix: agent-call-binding.ts#releaseAgentAfterCall, chamado explicitamente quando a chamada termina, com suporte a WRAP_UP intermediário antes de AVAILABLE.

Corrida entre duas campanhas reivindicando o mesmo agente

Sintoma (encontrado em revisão de código, não em produção): implementação inicial de claimAvailableAgent usava findFirst + update separado — janela de corrida entre duas campanhas concorrentes.

Fix: reescrito com UPDATE ... WHERE id = (SELECT ... FOR UPDATE SKIP LOCKED LIMIT 1) RETURNING ..., mesmo padrão atômico já usado para reserva de leads.

Diagnóstico geral

Para qualquer problema não listado acima:

  1. bash scripts/healthcheck.sh — descarta problema de infraestrutura básica primeiro.
  2. docker compose logs -f <serviço> — logs estruturados JSON, procurar por "level":50 (error) ou "level":40 (warn).
  3. docker exec b2bcall-asterisk asterisk -rx "..." — ver docs/ASTERISK.md para comandos úteis.
  4. Consultar AuditLog (GET /api/audit) se o problema envolve uma ação específica de um usuário.
  5. Verificar docker compose ps — um container "unhealthy" quase sempre aponta a causa raiz de problemas em cascata nos serviços que dependem dele.