docs: update coordinator project state

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Gustavo
2026-10-01 14:23:46 -03:00
co-authored by Claude Sonnet 5.5
parent 2724b90cb4
commit ce7af94d55
2 changed files with 230 additions and 235 deletions
+121 -235
View File
@@ -1,248 +1,134 @@
# Coordenador GLPI - Chamados Abertos
# Coordenador GLPI
Projeto n8n para atuar como coordenador diario de Suporte de TI, analisando chamados abertos no GLPI e enviando no Slack os 10 chamados que precisam de acao imediata.
Automação n8n que analisa os chamados abertos no GLPI todo dia útil, classifica
cada um com IA (contexto real do chamado, não só metadados) e envia no Slack os
10 chamados que realmente precisam de ação humana hoje.
## Objetivo
O repositório tem duas gerações do workflow:
Todos os dias uteis, o workflow consulta o banco do GLPI, calcula um score operacional para cada chamado aberto e envia uma lista priorizada no Slack.
- **V2** (`n8n/`, `logic/`) — versão atual, em produção. Todo este README
descreve o V2.
- **V1** (`Coordenador GLPI - Chamados Abertos Slack.n8n.json`, na raiz) —
versão legada, mantida apenas como rollback. Nunca foi importada nem ativada
na instância n8n de produção; existe só como referência histórica e plano B.
O foco nao e apenas listar chamados antigos. O ranking considera:
## Estado atual (verificado em 2026-08-25)
- SLA vencido ou proximo de vencer
- tempo sem interacao humana
- prioridade, urgencia e impacto
- idade do chamado
- chamado sem tecnico atribuido
- chamados em categorias e entidades reais do GLPI
```
Workflow: [DEV] Coordenador GLPI - Chamados Abertos v2 (Shadow) (id y6JFEnp7Z2IJEUe2)
active: true
SEND_ENABLED: true
ENVIRONMENT: production
FORCE_SEND: false (execução manual nunca envia de verdade sem essa flag)
Schedule: seg-sex 08:00, timezone America/Sao_Paulo (cron "0 8 * * 1-5")
Testes: 171/171 (node --test)
```
O nome do workflow no n8n ainda carrega o prefixo histórico `[DEV]` /
`(Shadow)` — isso é só rótulo visual, não reflete mais o estado real (ativo,
`SEND_ENABLED=true`, `ENVIRONMENT=production`). Renomear é seguro a qualquer
momento, mas não foi feito para não gerar um diff de auditoria sem função.
Se essa seção estiver desatualizada, o estado de verdade é sempre o que o n8n
responde (`n8n_get_workflow` / `n8n_manage_datatable`), nunca este arquivo.
## Arquitetura
```
GLPI (MySQL, somente leitura)
-> candidatos (pool configurável, sem corte artificial)
-> normalização de texto (decodifica HTML/entities do GLPI)
-> cache de classificação (Data Table, chave = hash do contexto + versão do classificador)
-> IA (OpenAI) classifica só quem mudou de contexto ou nunca foi analisado
-> score determinístico + seleção Top 10 com diversidade por grupo
-> guard de envio (SEND_ENABLED, FORCE_SEND, idempotência por hash da mensagem)
-> Slack (mensagem real, sem rodapé de atribuição do n8n)
-> shadow-diff contra a query legada do V1 (auditoria, não bloqueia envio)
```
Cada etapa grava em uma Data Table do n8n (`coordenador_glpi_memoria`,
`coordenador_glpi_envios_digest`, `coordenador_glpi_shadow_runs`) — não há
escrita nenhuma no banco do GLPI. Toda query GLPI é somente leitura; isso é
testado (`tools/build-workflow.test.js` varre `sql/*.sql` e o SQL gerado
procurando `INSERT/UPDATE/DELETE/ALTER/DROP/CREATE/TRUNCATE`).
### Por que a IA classifica o chamado, não só pontua
Cada chamado recebe uma classificação semântica (`ACTIONABLE_NOW`,
`UNASSIGNED_ACTIONABLE`, `WAITING_REQUESTER`, `WAITING_VENDOR`,
`WAITING_APPROVAL`, `SCHEDULED`, `DEPENDENCY_BLOCKED`, `NEEDS_COORDINATION`,
`PROJECT_LONG_TERM`, `DUPLICATE_OR_OBSOLETE`, `INSUFFICIENT_CONTEXT`) mais um
`next_action` concreto (nunca frases genéricas tipo "revisar chamado" — isso é
validado e rejeitado, ver `logic/classification.js`). O prompt
(`n8n/nodes/prepare-ai-prompt.node.js`) trata cronologia dos apontamentos como
obrigatória e distingue "precisa contatar alguém" (acionável agora) de "já
contatou e está esperando resposta" (`WAITING_*`).
### Determinismo do Top 10
`logic/diversity.js` desempata candidatos com score igual por `ticket.id`
(ascendente) — sem isso, ordens de chegada diferentes entre execuções
produziam Top10 diferentes para o mesmo dia, o que já causou um envio real
duplicado em teste (ver `docs/superpowers/notes/2026-08-25-relatorio-correcao-final.md`,
seção 5). Regressão coberta em `logic/diversity.test.js`.
## Arquivos
- `Coordenador GLPI - Chamados Abertos Slack.n8n.json`: workflow importavel no n8n
- `sql/top_10_chamados_abertos_glpi.sql`: query base usada pelo workflow
- `.env.example`: variaveis para subir n8n localmente
- `docker-compose.yml`: n8n local via Docker
## Como configurar
1. Suba o n8n local, se necessario:
```powershell
docker compose up -d
```
logic/*.js Lógica pura, testável com node:test (sem I/O, sem n8n)
n8n/nodes/*.node.js Drivers finos que rodam dentro de Code nodes do n8n
(cabeçalho "// @requires a.js b.js" declara quais
módulos de logic/ precisam ser inlinados)
n8n/workflow.template.json Fonte de verdade da estrutura do workflow
(marcadores __BUILD__:arquivo.js são substituídos
pelo conteúdo de logic/ na hora do build)
n8n/deployed/coordenador-glpi-v2.n8n.json Artefato gerado, importável no n8n
tools/build-workflow.js Inlina logic/ + n8n/nodes/ dentro do template
tools/build-workflow.test.js Regressões de build (dependência transitiva,
alwaysOutputData, columns.mappingMode, etc.)
sql/ Queries GLPI usadas como referência/validação manual
docs/superpowers/ Specs, planos e relatórios de execução (histórico,
não é para reescrever — cada nota é um registro de
um momento específico do projeto)
```
2. Acesse:
## Build
```text
http://localhost:5678
```bash
node tools/build-workflow.js
node --test
```
3. Importe o workflow:
```text
Coordenador GLPI - Chamados Abertos Slack.n8n.json
```
4. Configure a credencial MySQL no node:
```text
MySQL - Top 10 chamados abertos
```
5. Abra o node `Config - Coordenador GLPI` e preencha:
- `slack_webhook_url`: webhook do canal Slack
- `dias_sem_interacao_alerta`: padrao `2`
- `horas_sla_risco`: padrao `8`
6. Execute pelo `Manual Trigger` para validar.
7. Ative o workflow para rodar pelo `Schedule Trigger - Dias uteis 08h`.
## Metodologia de priorizacao
Cada chamado aberto recebe um `score_criticidade`, mas a lista final nao e mais apenas o top 10 geral. O workflow monta a lista por blocos para evitar que chamados muito antigos monopolizem todos os dias.
Blocos do top 10:
- 3 chamados com SLA vencido mais criticos
- 3 chamados com maior tempo sem interacao
- 2 chamados sem tecnico atribuido
- 2 chamados de prioridade alta mais recentes
Se algum bloco nao tiver chamados suficientes, o complemento vem do ranking geral.
O mesmo chamado nao entra duas vezes. Se ele ja apareceu em um bloco anterior, e removido dos blocos seguintes.
### Score
Pesos principais:
- SLA vencido: `+100`
- SLA vencendo nas proximas horas configuradas: `+45`
- chamado sem interacao ha 7 dias ou mais: `+35`
- chamado sem interacao ha 3 a 6 dias: `+25`
- chamado sem interacao ha 1 a 2 dias: `+10`
- prioridade GLPI: `priority * 8`
- urgencia GLPI: `urgency * 5`
- impacto GLPI: `impact * 5`
- idade do chamado: ate `+30`
- sem tecnico atribuido: `+25`
O resultado e ordenado por:
1. `score_criticidade` desc
2. chamados com SLA vencido primeiro
3. maior tempo sem interacao
4. chamados mais antigos
## Reincidencia
O workflow controla reincidencia usando `staticData` global do n8n, sem gravar nada no banco do GLPI.
Regra:
- apareceu 1 vez: normal
- apareceu 2 dias seguidos: marcado como reincidente
- apareceu 3 dias seguidos ou mais: escalar para coordenacao
Campos salvos no `staticData`:
- `ultima_data_disparo`
- `ultimos_chamados_enviados`
- `reincidencia_chamados`
Se o workflow for duplicado, recriado ou importado como novo workflow, esse historico pode ser perdido porque pertence ao workflow no n8n.
## Tempo sem interacao
O campo `dias_sem_interacao` considera a maior data encontrada entre:
- followups publicos do chamado: `date`, `date_mod`, `date_creation`
- tarefas do chamado: `date`, `date_mod`, `date_creation`
- modificacao do chamado: `glpi_tickets.date_mod`
- abertura do chamado: `glpi_tickets.date`
Isso evita marcar um chamado como parado quando alguem acabou de adicionar ou alterar uma tarefa no GLPI, mesmo que a tarefa esteja planejada para outro dia.
Se a maior data encontrada estiver no futuro por causa de uma tarefa planejada, o contador fica em `0` dias sem interacao, evitando valores negativos no relatorio.
## Regra de chamados abertos
O SQL considera abertos todos os chamados com:
```sql
is_deleted = false
AND status NOT IN (5, 6)
```
Este gatilho principal ignora chamados das categorias:
- `ADMINISTRATIVO`
- `DESENVOLVIMENTO`
- `INFRA`
O filtro usa o nome completo da categoria (`completename`) ou o nome simples (`name`) iniciando com esses termos, entao tambem remove subcategorias dessas filas.
No GLPI, normalmente:
- `5`: solucionado
- `6`: fechado
## Campos usados
A query usa tabelas comuns do GLPI:
- `glpi_tickets`
- `glpi_itilcategories`
- `glpi_entities`
- `glpi_itilfollowups`
- `glpi_tickettasks`
- `glpi_tickets_users`
- `glpi_users`
Se sua versao do GLPI tiver customizacoes, valide primeiro a query `sql/top_10_chamados_abertos_glpi.sql` diretamente no banco.
## Saida no Slack
O Slack recebe uma mensagem curta com:
- top 10 chamados para atacar hoje
- tecnico atribuido, quando existir
- SLA
- dias sem interacao
- dias aberto
- reincidencia
- link do chamado, se `glpi_base_url` estiver configurado no workflow
Campos removidos da mensagem para reduzir ruido:
- motivo
- acao
- prazo
Se o agente de IA falhar, o workflow envia a mensagem curta gerada por codigo, sem depender da IA.
## Agente de IA do n8n com Z IA
O workflow usa o node:
```text
AI Agent - Revisar mensagem
```
Por padrao, ele esta conectado ao node:
```text
Z IA Chat Model
```
Esse node usa o chat model compativel com OpenAI do n8n, mas a credencial deve ser configurada para a Z IA.
A IA nao monta mais a mensagem do zero. O node `Code - Reincidencia e mensagem curta` monta a mensagem final de forma deterministica, e a IA apenas revisa clareza e reduz texto.
Regras do prompt:
- nao adicionar fatos
- nao criar motivo
- nao criar acao
- nao criar prazo
- nao alterar IDs, tecnicos, datas, SLA, dias, reincidencia ou links
- nao usar tom agressivo
No n8n, abra o node:
```text
Z IA Chat Model
```
Configure:
- credencial `Z IA`
- endpoint/base URL da Z IA, se a credencial permitir
- modelo correto disponibilizado pela Z IA, por exemplo `glm-4.5-flash`
No arquivo importavel deixei o modelo como `glm-4.5-flash`. Se sua conta Z IA usar outro nome de modelo, troque pelo nome exato exibido na plataforma.
Se aparecer `The resource you are requesting could not be found`, revise principalmente:
- Base URL da credencial: normalmente `https://api.z.ai/api/paas/v4`
- Modelo: precisa existir na sua conta, por exemplo `glm-4.5-flash`
- Chave API: precisa ser da Z IA, nao da OpenAI
- Node usado: mantenha `Z IA Chat Model` conectado ao `AI Agent - Revisar mensagem`
Se a Z IA aparecer no seu n8n como node proprio, mantenha o `AI Agent - Revisar mensagem` e substitua somente o subnode `Z IA Chat Model` pelo modelo oficial da Z IA.
## Recomendacao direta
Nao trate isso como relatorio bonito. Trate como fila diaria de comando.
Minha sugestao operacional:
- rodar as 08h no canal da lideranca de suporte
- exigir responsavel e prazo para cada um dos 10 chamados ate 10h
- repetir apenas os chamados que continuarem parados no dia seguinte
- se um chamado aparecer 3 dias seguidos, escalar para coordenacao/gestao
- separar um segundo workflow para chamados sem tecnico atribuido, porque isso e falha de triagem, nao backlog comum
- criar outro alerta intradiario para SLA vencendo nas proximas 2 horas
Se quiser resultado real, nao basta notificar. Tem que transformar a mensagem do Slack em ritual diario de cobranca.
O build só precisa rodar de novo quando `logic/*.js`, `n8n/nodes/*.node.js` ou
`n8n/workflow.template.json` mudarem. O artefato gerado
(`n8n/deployed/coordenador-glpi-v2.n8n.json`) **não é a fonte de verdade do
ambiente de produção** — o workflow ativo no n8n foi promovido diretamente via
API (`n8n_update_partial_workflow`) e hoje diverge do template em 3 valores de
configuração de propósito:
| campo | template (default de deploy limpo) | live (produção) |
|---|---|---|
| `ENVIRONMENT` | `dev` | `production` |
| `SEND_ENABLED` | `false` | `true` |
| `FORCE_SEND` | `false` | `false` |
Isso é intencional: o template continua "seguro por padrão" para qualquer
reimportação futura (nunca envia sozinho), e a promoção para produção é um ato
explícito e auditável via `n8n_update_partial_workflow`, não algo que um
rebuild acidental possa reverter. Se um dia o workflow live for recriado do
zero a partir do template, repita a promoção manualmente (ver comando na nota
`2026-08-25-relatorio-finalizacao-producao.md`).
## Rollback
O V1 (`Coordenador GLPI - Chamados Abertos Slack.n8n.json`) nunca foi
importado no n8n de produção — não há nada para "reverter para V1", ele
continua disponível como plano B caso seja necessário abandonar o V2. Para
desativar o V2 sem tocar em nada mais: `SEND_ENABLED=false` no node
`Config - Coordenador GLPI v2` e/ou desativar o workflow.
## Credenciais e segredos
Nenhum segredo vive neste repositório. Tudo (Slack bot token, OpenAI API key,
credencial MySQL do GLPI) é gerenciado pelo credential store do próprio n8n e
referenciado nos nodes só por `{id, name}`.
+109
View File
@@ -0,0 +1,109 @@
# Estado do Projeto — Coordenador GLPI
Última atualização: 2026-09-27 (revisão de documentação; último estado live
verificado por execução real: 2026-08-25).
## O que é
Workflow n8n que roda seg-sex 08:00 (`America/Sao_Paulo`), lê chamados abertos
do GLPI (MySQL, read-only), classifica cada um com IA e envia no Slack os 10
que precisam de ação humana hoje. Duas gerações no repo: **V2** (ativo,
`n8n/` + `logic/`) e **V1** (legado, `Coordenador GLPI - Chamados Abertos
Slack.n8n.json` na raiz — nunca foi importado no n8n de produção, existe só
como rollback).
## Estado de produção (n8n instance, workflow id `y6JFEnp7Z2IJEUe2`)
Confirmado por `n8n_get_workflow` em 2026-08-25 após ativação explícita
autorizada pelo usuário ("pode ativar em produção"):
- `active: true`
- Config node: `ENVIRONMENT=production`, `SEND_ENABLED=true`, `FORCE_SEND=false`
- Schedule Trigger conectado, cron `0 8 * * 1-5`, `settings.timezone=America/Sao_Paulo`
- 43 nodes, nenhum webhook temporário de debug presente
- Credenciais live: MySQL `GLPI` (id `dsVj1nmOIXRvix9n`), Slack `Slack - Bot
Coordenador (Chamados Suporte)` (id `lNqwBBrtLfkRqw2X`), OpenAI `OpenAI -
Coordenador GLPI` (id `WUGriAn5FgCiLza2`)
- Slack destino: canal `chamados-suporte` (id `C0BJNBTPFT5`)
**Este arquivo não substitui uma verificação live.** Se for tomar qualquer
decisão que dependa do estado atual (reativar, mudar config, investigar um
envio), reconfirme com `n8n_get_workflow` / `n8n_executions` antes — não
assuma que nada mudou desde 25/08 só porque este arquivo diz isso.
### Limpeza pós-ativação (2026-08-25)
Durante a fase de correção final, execuções de teste reais enviaram mensagens
ao Slack para ~37 tickets, gravando `last_sent_at`/`times_sent`/
`consecutive_days_sent` na Data Table `coordenador_glpi_memoria`. Isso foi
resetado manualmente (`last_sent_at`, `times_sent`, `times_sent_30d`,
`consecutive_days_sent` → `null` para essas linhas) para que o primeiro envio
real de produção não sofresse penalidade de cooldown por causa do teste.
`last_context_hash`/`ai_classification`/`next_action` foram preservados (não
há motivo para reclassificar de novo o que não mudou no GLPI).
## Arquitetura (resumo — detalhe completo no README.md)
```
GLPI (MySQL read-only) -> candidatos -> normalização -> cache de classificação
(Data Table, chave = hash do contexto + AI_CLASSIFIER_VERSION) -> IA (OpenAI,
só quem mudou) -> score determinístico -> Top10 com diversidade por grupo
(desempate por ticket.id — ver "bugs reais corrigidos") -> guard de envio
(SEND_ENABLED + FORCE_SEND + idempotência por hash da mensagem) -> Slack
-> shadow-diff contra query legada do V1 (auditoria, não bloqueia)
```
`logic/*.js` é lógica pura testável (`node --test`, 171/171 passando em
2026-09-27). `n8n/nodes/*.node.js` são drivers finos que `tools/build-workflow.js`
inlina dentro de `n8n/workflow.template.json` para gerar
`n8n/deployed/coordenador-glpi-v2.n8n.json`.
**O template local (`ENVIRONMENT=dev`, `SEND_ENABLED=false`) diverge de
propósito do live (`production`/`true`)** — ver README.md seção "Build" para
a tabela completa e o motivo (template = default seguro para reimportação;
promoção para produção é ato manual e auditável, não algo que rebuild reverte).
## Bugs reais corrigidos (histórico — não reabrir sem nova evidência)
1. `WAITING_*`/bloqueados entrando no Top10 via `contextChanged` — corrigido
em `logic/cooldown.js`.
2. Rótulo fixo "Validar e executar" sempre vencendo o `next_action` real da
IA — corrigido em `logic/message.js` (`shortActionLabel`).
3. **Crítico**: Top10 não-determinístico (empate de score desempatado pela
ordem de chegada dos itens, que o n8n não garante entre branches
paralelos) causou envio real duplicado com conteúdo diferente no mesmo
dia. Corrigido com desempate por `ticket.id` em `logic/diversity.js`.
Regressão em `logic/diversity.test.js`.
4. `alwaysOutputData` faltando em `Data Table - Buscar Envios Recentes`
quebraria o caminho feliz todo dia sem envio prévio (0 linhas = n8n não
roda os nodes seguintes).
5. `columns.mappingMode` faltando nos nodes de insert da Data Table de envio
(`Cannot convert undefined or null to object`).
6. Rodapé de atribuição automática do n8n no Slack (`includeLinkToWorkflow`)
— desligado explicitamente.
7. Falta de escopo OAuth (`channels:join`) causando `not_in_channel` mesmo
com o bot já no canal — resolvido reinstalando o app Slack com o escopo.
Detalhe completo de cada um, com execução real que provou a causa raiz, em
`docs/superpowers/notes/2026-08-25-relatorio-correcao-final.md` e
`2026-08-25-relatorio-finalizacao-producao.md`.
## Restrições que continuam valendo (não renegociar sem pedido explícito do usuário)
- GLPI é estritamente somente leitura — nenhuma escrita, nunca.
- V1 nunca é apagado; é o rollback.
- Não recalibrar pesos de score/diversidade/pool sem bug real comprovado.
- Segredos só via credential store do n8n — nunca em arquivo do repo.
- Execução manual nunca envia de verdade sem `FORCE_SEND=true` (proteção
contra teste acidental em produção).
## Onde olhar primeiro
- Dúvida sobre comportamento atual do workflow → `n8n_get_workflow` (live),
não os arquivos deste repo (o repo é a fonte de verdade da *lógica*, não
necessariamente da config live, ver seção "Build" do README).
- Dúvida sobre por que uma decisão de design foi tomada →
`docs/superpowers/specs/2026-08-24-coordenador-glpi-v2-design.md` e
`docs/superpowers/plans/2026-08-24-coordenador-glpi-v2.md`.
- Dúvida sobre um bug específico já corrigido → notas datadas em
`docs/superpowers/notes/`, mais recente primeiro.