Files
Projeto-Portaria/prompts/FASE-7-escalonamento.md
2026-07-22 15:55:55 -03:00

4.9 KiB
Raw Blame History

FASE 7 — Escalonamento, fila de operador e recado

Objetivo

Completar a retaguarda: escalonamento entre moradores, fila de operador humano com contexto, app do operador, e recado em vídeo. É o que transforma "chamada não atendida" em "visita resolvida".

Pré-requisitos

FASES 06 concluídas. Ler docs/03-FLUXOS-E-CONTRATOS.md §4 e docs/00-VISAO-E-PRODUTO.md §1.

Por que esta fase existe

A tese do produto é que o morador atende. A realidade é que ele nem sempre atende — gente em reunião, dirigindo, dormindo. É por isso que o mercado consolidado usa operador humano 24h.

Esta fase é o que torna a aposta vendável: quando o morador não atende, o visitante não fica sem saída. E é o que sustenta comercialmente o plano Assistido.

Tarefas

1. Escalonamento entre moradores

VISIT_RING_TIMEOUT (20s) → ESCALONADA. Toca para os demais unit_members da unidade, em paralelo, respeitando ring_order para ordenar a exibição e receives_calls.

O primeiro que atender vence; os demais recebem cancelamento da notificação — deixar notificação órfã tocando depois de resolvido é ruído que faz o morador desativar o app.

2. Fila de operador

Ativa apenas com tenant_features.operator_queue habilitado. Sem o módulo, ESCALONADA vai direto para RECADO_EM_VIDEO.

Fila ordenada por tempo de espera, com prioridade para VISITA sobre ENTREGA. POST /operator/queue/claim usa FOR UPDATE SKIP LOCKED — dois operadores nunca pegam a mesma visita.

Posição na fila enviada ao visitante por QUEUE_POSITION no WebSocket. Esperar é tolerável; esperar sem saber quanto falta, não.

VISIT_QUEUE_TIMEOUT (120s) → RECADO_EM_VIDEO.

3. App do operador

Mesmo binário Compose Multiplatform, perfil decidido por GET /me. Layout mais denso que o do morador.

Fila à esquerda: foto, tipo, unidade, tempo de espera e por que escalonou (ninguém atendeu / quiet hours / unidade inexistente).

Contexto à direita, antes de assumir: histórico da unidade, visitas recentes, regra de entrega, nomes dos moradores, telefone. O operador nunca atende sem contexto — atender às cegas é o que faz a portaria remota parecer pior que a física.

Presença: DISPONIVEL / EM_ATENDIMENTO / OFFLINE, com heartbeat. Operador que perde conexão volta para OFFLINE automaticamente e suas visitas retornam à fila.

Ao resolver, o operador registra resolution_reason — obrigatório, e é o que alimenta a análise de por que o modelo autônomo falhou naquele caso.

4. Recado em vídeo

RECADO_EM_VIDEO: o visitante grava até 30s. Salvo em media_assets com kind = RECADO_VIDEO e retenção de 30 dias.

Notificação ao morador sem urgência — não é CallKit, não é full-screen intent. É pendência, não chamada.

Aparece no topo da home do app até ser resolvida. O morador pode autorizar retroativamente (gerando access_grant com validade estendida), negar, ou apenas marcar como visto.

5. Quiet hours

Dentro de condominiums.quiet_hours, visitas não pré-autorizadas pulam o toque ao morador: vão direto para FILA_OPERADOR (se o módulo estiver ativo) ou RECADO_EM_VIDEO.

É a defesa contra tocar em todos os apartamentos de madrugada, e precisa ser óbvia para o visitante: "Fora do horário. Transferindo para a portaria."

6. Cancelamento de notificações

Ao resolver a visita, cancele as notificações pendentes em todos os dispositivos que foram acionados. Android usa notificationId estável por visita; iOS usa CXProvider.reportCall(with:endedAt:reason:).

Uma chamada CallKit que continua tocando depois de resolvida é bug grave de percepção — o morador acha que o app está quebrado.

7. Métricas de escalonamento

portaria.escalation.ratio (quantas visitas escalaram) · portaria.queue.wait_time · portaria.queue.abandoned · portaria.message.recorded · portaria.message.unresolved_24h.

escalation.ratio é a métrica que decide se um condomínio fica no plano Autônomo ou precisa migrar para o Assistido.

Critérios de aceite

  • Escalonamento toca para os demais moradores após 20s
  • Primeiro a atender vence; notificações dos outros são canceladas
  • Sem operator_queue, escalonada vai direto para recado
  • Dois operadores não pegam a mesma visita (teste concorrente)
  • Posição na fila chega ao visitante em tempo real
  • Operador que perde conexão devolve a visita à fila
  • Recado gravado, armazenado e notificado sem urgência
  • Recado aparece como pendência na home até resolver
  • Quiet hours desvia sem tocar em ninguém, com aviso ao visitante
  • CallKit não continua tocando após resolução
  • Fluxo completo sem morador algum: QR → escalonamento → fila → operador → autorizado

Não faça nesta fase

  • Pré-autorização e recorrentes (v2)
  • WhatsApp (FASE 8)
  • Escalação para telefone/PSTN