chore: import coordenador (subtree)
git-subtree-dir: coordenador git-subtree-mainline:facbe2e340git-subtree-split:ce7af94d55
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
# Coordenador GLPI
|
||||
|
||||
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.
|
||||
|
||||
O repositório tem duas gerações do workflow:
|
||||
|
||||
- **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.
|
||||
|
||||
## Estado atual (verificado em 2026-08-25)
|
||||
|
||||
```
|
||||
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
|
||||
|
||||
```
|
||||
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)
|
||||
```
|
||||
|
||||
## Build
|
||||
|
||||
```bash
|
||||
node tools/build-workflow.js
|
||||
node --test
|
||||
```
|
||||
|
||||
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}`.
|
||||
Reference in New Issue
Block a user