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).
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):
- O cálculo de concorrência total não incluía chamadas já conectadas ao
agente (
agentConnectedCalls), subestimando quanto já estava "em uso". - O ajuste de pacing subia mesmo sem nenhuma atividade de discagem no ciclo (nada "deu errado", então ele interpretava como "pode subir mais").
- 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:
bash scripts/healthcheck.sh— descarta problema de infraestrutura básica primeiro.docker compose logs -f <serviço>— logs estruturados JSON, procurar por"level":50(error) ou"level":40(warn).docker exec b2bcall-asterisk asterisk -rx "..."— verdocs/ASTERISK.mdpara comandos úteis.- Consultar
AuditLog(GET /api/audit) se o problema envolve uma ação específica de um usuário. - Verificar
docker compose ps— um container "unhealthy" quase sempre aponta a causa raiz de problemas em cascata nos serviços que dependem dele.