docs: add V2 design spec (approved)

docs/superpowers/specs/2026-08-24-coordenador-glpi-v2-design.md
Covers Etapa 0 recovery findings, architecture, data model, AI schema,
scoring, message format, shadow mode. Approved by user in chat.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Gustavo
2026-08-24 15:00:34 -03:00
co-authored by Claude Sonnet 5
parent 1eb1f80b4f
commit de147107d5
@@ -0,0 +1,77 @@
# Coordenador GLPI V2 — Seleção acionável por IA + regras determinísticas
Status: aprovado pelo usuário em 2026-08-24. Implementação em DEV, shadow mode.
## Contexto (Etapa 0 — o que existe hoje)
Projeto 100% n8n, sem backend tradicional:
- `Coordenador GLPI - Chamados Abertos Slack.n8n.json`: workflow importável (V1).
- `sql/top_10_chamados_abertos_glpi.sql`: query base (duplicada dentro do node `Code - Montar SQL Top 10`).
- `docker-compose.yml` + `.env(.example)`: n8n local (Docker Desktop indisponível neste ambiente de execução; não foi possível subir localmente).
- `tools/`: pasta vazia, reservada — reaproveitada nesta V2 para o script de build do workflow.
- Repositório git local sem nenhum commit (tudo untracked). Nada a preservar de histórico.
### Lógica V1 (comprovada, reduziu backlog de ~167 para ~90)
- Trigger: `Schedule Trigger` cron `0 8 * * 1-5` (+ `Manual Trigger` para teste).
- Node `Config - Coordenador GLPI` (Set): `slack_webhook_url`, `glpi_base_url`, `dias_sem_interacao_alerta` (default 2), `horas_sla_risco` (default 8).
- Node `Code - Montar SQL Top 10` monta a query com esses parâmetros e executa via node MySQL (credencial `MySQL GLPI`, nunca configurada em nenhum ambiente acessível a mim).
- SQL: filtra `is_deleted=false AND status NOT IN (5,6)` e exclui categorias `ADMINISTRATIVO%`, `DESENVOLVIMENTO%`, `INFRA%` (via `completename`/`name`). Calcula `dias_aberto`, `dias_sem_interacao` (maior data entre followups públicos, tarefas, `date_mod`, `date` — nunca negativo), `sla_vencido`, `sla_em_risco`, `sem_tecnico`. Score: SLA vencido +100, SLA em risco +45, sem interação (+35/+25/+10 por faixa), idade até +30, prioridade×8, urgência×5, impacto×5, sem técnico +25. Seleção final em blocos (3 SLA vencido, 3 sem interação, 2 sem técnico, 2 prioridade alta recente, complemento pelo ranking geral), sem repetir chamado entre blocos, `LIMIT 10`.
- Node `Code - Reincidencia e mensagem curta`: reincidência via `$getWorkflowStaticData('global')` (chamado apareceu N dias seguidos: 1 normal, 2 reincidente, 3+ escalar). Monta mensagem determinística e um `prompt_ia`.
- `AI Agent - Revisar mensagem` (LangChain agent + `Z IA Chat Model`, GLM-4.5-flash via endpoint compatível OpenAI): só revisa clareza/reduz texto, proibido inventar fato/motivo/ação/prazo.
- `Code - Preparar mensagem Slack`: valida que a saída da IA preserva todos os `#id` e links e não contém campos proibidos (`motivo/ação/prazo`); se falhar, usa o texto determinístico puro (fallback já existente e funcional).
- `HTTP - Slack enviar mensagem`: POST no webhook configurado no Config node.
### Causa técnica do "loop" (chamados repetindo o Top 10 sem ação real)
O score é dominado por sinais que só crescem com o tempo (`dias_sem_interacao`, SLA vencido, idade) e a reincidência do V1 é só um contador informativo — não penalizada no score nem interrompida por cooldown. Um chamado com SLA vencido há semanas e sem ação prática do suporte continua pontuando alto todos os dias e reentra nos blocos "SLA vencido"/"sem interação", empurrando candidatos com ação real (mas sinais mais baixos) para fora do Top 10.
### Ambiente DEV confirmado
Instância n8n real e acessível via MCP: `https://devn8n.gtigrupo.com.br` (73 workflows). Não existe lá nenhum workflow do "Coordenador GLPI - Top 10"; o que existe de GLPI é um chatbot de abertura de chamados via WhatsApp (`[DEV] GTI - GLPI x WhatsApp (Z-PRO) v7`, ativo, 52 nodes) e um sub-workflow arquivado de lock/unlock de entidade (`Enforcer - GLPI`). Ambos **fora de escopo** — não serão alterados. Não há credencial MySQL do GLPI nem de IA "Z IA" nesse ambiente.
Decisões do usuário (2026-08-24):
- Credencial MySQL: nome `MySQL GLPI - Coordenador`, criada pelo usuário diretamente no n8n DEV.
- Credencial IA: nova credencial `OpenAI - Coordenador GLPI` (id `WUGriAn5FgCiLza2`, tipo `openAiApi`) criada por mim com chave-placeholder óbvia; o usuário substitui o valor real direto na UI do n8n. Provedor: OpenAI (GPT), não Z IA, não reaproveitar credencial existente de outro workflow.
- Nenhum envio real (Slack/WhatsApp) sem destino seguro definido — por padrão, shadow mode só grava/loga, nunca publica externamente.
## Objetivo da V2
Trocar "quais chamados são mais problemáticos" por "quais chamados têm ação concreta que o suporte consegue executar agora", mantendo Top 10, mensagem curta (mais curta que hoje) e todas as regras já comprovadas (exclusão administrativa, SLA, prioridade).
## Arquitetura
Workflow novo e separado no n8n DEV — **V1 permanece intocado** (local e não importado em nenhum ambiente acessível):
`[DEV] Coordenador GLPI - Chamados Abertos v2 (Shadow)`, inativo por padrão, disparado por Manual Trigger durante a validação.
Pipeline (7 estágios):
1. **Seleção inicial**: SQL reaproveita a base V1 (mesmas tabelas, mesmo `dias_sem_interacao`, mesma exclusão administrativa) mas remove o corte de blocos/`LIMIT 10` — devolve até `CANDIDATE_POOL_SIZE` (padrão configurável, começar em 60) candidatos ordenados pelo `score_criticidade` barato atual. Este número é o tamanho do funil, não o volume que vai para a IA.
2. **Filtros determinísticos**: elegibilidade por status, administrativo (já no SQL), duplicidade.
3. **Memória/cache**: n8n Data Table `coordenador_glpi_memoria` (não `staticData` — resolve a fragilidade documentada no README do V1 de perder histórico ao reimportar o workflow). Colunas: `ticket_id, last_analyzed_at, last_sent_at, last_context_hash, last_meaningful_change_at, ai_classification, ai_confidence, blocker_type, waiting_for, next_action, next_review_at, times_sent, times_sent_30d, consecutive_days_sent, last_score, last_ai_reason, updated_at`.
4. **Hash de contexto**: função JS pura (sem `crypto` do Node — mais portável em Code node) sobre campos que só mudam por evento real (status, técnico, grupo, último followup/tarefa por id+data, solução, pendência, aprovação, vínculos). Nunca inclui `dias_aberto`/`dias_sem_interacao`.
5. **Coleta de contexto**: só para candidatos sem cache válido (hash mudou ou passou `next_review_at`). Usa tabelas já confirmadas (`glpi_itilfollowups`, `glpi_tickettasks`, `glpi_tickets_users`, `glpi_users`, `glpi_entities`, `glpi_itilcategories`). Tabelas adicionais candidatas (soluções, aprovações, chamados vinculados) só entram depois de confirmadas por introspecção real no banco (`SHOW TABLES`/`DESCRIBE`) quando a credencial MySQL estiver disponível — nunca assumidas de antemão. Cada fonte opcional é isolada com fallback gracioso (se a tabela não existir/,divergir, o pipeline continua sem ela e registra isso no log).
6. **Classificação IA**: só os candidatos sem cache válido chegam aqui (estágio 4), até o teto `AI_MAX_CANDIDATES` (padrão 30 — proteção de custo/latência por execução; se sobrarem mais candidatos elegíveis que o teto, prioriza por `score_criticidade` e deixa o resto para a próxima execução via `next_review_at`). HTTP direto à API da OpenAI (Chat Completions com `response_format: json_schema`) — mesmo padrão já usado no workflow `[DEV] GTI - GLPI x WhatsApp v7` deste n8n (não o `AI Agent`/LangChain do V1, que só polia texto). Classes: `ACTIONABLE_NOW, UNASSIGNED_ACTIONABLE, WAITING_REQUESTER, WAITING_VENDOR, WAITING_APPROVAL, SCHEDULED, DEPENDENCY_BLOCKED, NEEDS_COORDINATION, PROJECT_LONG_TERM, DUPLICATE_OR_OBSOLETE, INSUFFICIENT_CONTEXT`. Schema conforme o documento do usuário (`classification, actionable_today, confidence, blocker_type, waiting_for, next_action, suggested_action_type, business_risk, reason, evidence`). Validação estrita em código; JSON inválido/timeout/erro → fallback determinístico (log explícito de fallback, memória não corrompida).
7. **Regras determinísticas + score + ranking**: acionabilidade IA como fator dominante; SLA/idade continuam pesando mas não dominam sozinhos; penalidade forte para bloqueio externo/`WAITING_*`/`NEEDS_COORDINATION`/reenvio sem mudança (cooldown crescente); exceção permite prioridade crítica furar cooldown. Diversidade leve entre grupos conceituais. Top 10 fixo (não mexe no volume nesta fase).
8. **Mensagem final**: formato curto (exemplo no design aprovado), sem score/confiança/justificativa longa. Envio real desligado por padrão (`SHADOW_MODE=true`): grava em Data Table/log, não publica em canal nenhum até haver destino seguro confirmado pelo usuário.
9. **Bloqueados/coordenação**: saída separada, só com novidades/mudanças desde a última vez (não repete lista idêntica).
10. **`INSUFFICIENT_CONTEXT`**: telemetria própria (quais chamados, por quê, o que falta).
## Configuração (env vars no estilo já usado no n8n desta org, padrão `env(K, default)`)
`AI_ENABLED, AI_PROVIDER=openai, AI_MODEL, CANDIDATE_POOL_SIZE=60, AI_MAX_CANDIDATES=30, TOP_N=10, COOLDOWN_DAYS, AI_CONFIDENCE_THRESHOLD, AI_MAX_CONTEXT_CHARS, DEV_MODE=true, SHADOW_MODE=true`.
## Testes
Sem framework de testes no projeto hoje. Lógica determinística pura (hash, cooldown, score, elegibilidade, diversidade, formatação de mensagem) extraída para `logic/*.js`, testada com o runner nativo do Node (`node:test`, zero dependência nova). Script em `tools/` monta o JSON do workflow a partir desses módulos (fonte única, sem divergência entre o testado e o que roda no n8n). Cobrir os 17 casos do documento do usuário (seção 22).
## Shadow mode
Mesma execução manual roda a query legado (V1, sem corte) e a lógica nova lado a lado, grava os dois rankings e o diff (quem entrou/saiu e por quê) em Data Table + endpoint de debug (mesmo padrão `Debug - Ver Log`/`Debug - Montar Painel` já usado no workflow de WhatsApp desse n8n).
## Fora de escopo / não fazer
Produção; qualquer workflow do n8n DEV não relacionado a este projeto (WhatsApp GLPI, Enforcer, Klingo, ClinicAll, SysLife, ZPRO, Asaas etc.); reduzir o Top 10 para menos de 10; aumentar o tamanho da mensagem; deixar a IA decidir sozinha; reprocessar todo dia sem necessidade; escrever diretamente em tabelas do GLPI.