Files
SLACK_COORD_RELATORIO/n8n/workflow.template.json
T
GustavoandClaude Sonnet 5 ea105d525c fix(v2): three real bugs found by an actual end-to-end run against production GLPI data
Ran the deployed workflow for real (temporary webhook trigger, briefly
activated, immediately deactivated and removed after each attempt) against
the real GLPI MySQL database and a real OpenAI credential. Found and fixed:

1. $env access is blocked on this n8n instance (N8N_BLOCK_ENV_ACCESS_IN_NODE).
   parseConfig($env) threw "access to env vars denied" on the very first
   Code node. Config now comes from the "Config - Coordenador GLPI v2" Set
   node instead (same pattern the V1 workflow already used) -- expanded
   that node with all 13 tunable parameters, and pointed the 3 affected
   node drivers (build-sql-candidatos, decide-needs-ai, select-top10) at
   it instead of $env.

2. Data Table "upsert" operation requires both `columns` (values) and a
   separate `filters` (match condition) -- I had only set columns with
   matchingColumns, which isn't enough; n8n refused to activate the
   workflow ("Missing or invalid required parameters: filters"). Added
   filters.conditions matching on ticket_id to both memory-writing Data
   Table nodes.

3. Real architecture bug: "Data Table - Buscar Memoria" sat inline in the
   41-candidate chain (Mesclar Contexto -> Buscar Memoria -> Decidir
   Necessidade de IA). A bulk "get all rows" Data Table node run with 41
   input items produced 41 identical fetches and its OWN output (the empty
   memory table on a first run) replaced the 41 candidates entirely --
   "Decidir Necessidade de IA" received 0 items and the whole main branch
   silently skipped. Moved the memory fetch to a parallel branch off
   Config (fires once) and added a real Merge node ("Merge - Aguardar
   Memoria") to guarantee both the candidates and the memory rows have
   executed before "Decidir Necessidade de IA" runs -- same synchronization
   pattern already used for context and for the shadow comparison.

Also fixed a cosmetic bug found in the real message output: buildTopMessage
produced a quadruple newline between the header and the first ticket block
([header, '', ...blocks].join('\n\n') double-counts the blank line already
inserted by the '\n\n' join). Added a regression test.

Verified with a real execution (id 13599, 24/08/2026 19:25 UTC) against the
live GLPI database: 41 real candidates, 30 sent to OpenAI (capped), 11
fallback-capped, memory table populated with 41 real rows including
genuine WAITING_VENDOR/WAITING_REQUESTER classifications with real
blocker_type/waiting_for fields extracted from real ticket task content,
and a real shadow_runs row comparing Legacy x V2 (4 tickets removed by V2:
2 for INSUFFICIENT_CONTEXT, 1 WAITING_REQUESTER, 1 WAITING_VENDOR; 4 added).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 16:30:13 -03:00

576 lines
20 KiB
JSON

{
"name": "[DEV] Coordenador GLPI - Chamados Abertos v2 (Shadow)",
"nodes": [
{
"id": "manual-trigger-v2",
"name": "Manual Trigger",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [-1600, 0],
"parameters": {}
},
{
"id": "schedule-trigger-v2-desligado",
"name": "Schedule Trigger - Dias uteis 08h (desligado em DEV)",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.3,
"position": [-1600, 900],
"parameters": {
"rule": {
"interval": [
{ "field": "cronExpression", "expression": "0 8 * * 1-5" }
]
}
}
},
{
"id": "config-v2",
"name": "Config - Coordenador GLPI v2",
"type": "n8n-nodes-base.set",
"typeVersion": 3.5,
"position": [-1360, 0],
"parameters": {
"assignments": {
"assignments": [
{ "id": "glpi-base-url-v2", "name": "glpi_base_url", "type": "string", "value": "https://glpi.suaempresa.com.br" },
{ "id": "cfg-ai-enabled", "name": "AI_ENABLED", "type": "boolean", "value": true },
{ "id": "cfg-ai-provider", "name": "AI_PROVIDER", "type": "string", "value": "openai" },
{ "id": "cfg-ai-model", "name": "AI_MODEL", "type": "string", "value": "gpt-4o-mini" },
{ "id": "cfg-candidate-pool-size", "name": "CANDIDATE_POOL_SIZE", "type": "number", "value": 60 },
{ "id": "cfg-ai-max-candidates", "name": "AI_MAX_CANDIDATES", "type": "number", "value": 30 },
{ "id": "cfg-top-n", "name": "TOP_N", "type": "number", "value": 10 },
{ "id": "cfg-cooldown-days", "name": "COOLDOWN_DAYS", "type": "number", "value": 1 },
{ "id": "cfg-ai-confidence-threshold", "name": "AI_CONFIDENCE_THRESHOLD", "type": "number", "value": 0.6 },
{ "id": "cfg-ai-max-context-chars", "name": "AI_MAX_CONTEXT_CHARS", "type": "number", "value": 4000 },
{ "id": "cfg-dev-mode", "name": "DEV_MODE", "type": "boolean", "value": true },
{ "id": "cfg-shadow-mode", "name": "SHADOW_MODE", "type": "boolean", "value": true },
{ "id": "cfg-dias-sem-interacao-alerta", "name": "DIAS_SEM_INTERACAO_ALERTA", "type": "number", "value": 2 },
{ "id": "cfg-horas-sla-risco", "name": "HORAS_SLA_RISCO", "type": "number", "value": 8 }
]
},
"options": {}
}
},
{
"id": "code-montar-sql-candidatos",
"name": "Code - Montar SQL Candidatos",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [-1120, -160],
"parameters": { "jsCode": "__BUILD__:build-sql-candidatos.node.js" }
},
{
"id": "code-montar-sql-legado",
"name": "Code - Montar SQL Legado",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [-1120, 900],
"parameters": { "jsCode": "__BUILD__:build-sql-legado.node.js" }
},
{
"id": "mysql-buscar-candidatos",
"name": "MySQL - Buscar Candidatos",
"type": "n8n-nodes-base.mySql",
"typeVersion": 2.5,
"position": [-880, -160],
"parameters": { "operation": "executeQuery", "query": "={{ $json.query }}", "options": {} }
},
{
"id": "mysql-legado-top10-shadow",
"name": "MySQL - Legado Top 10 (Shadow)",
"type": "n8n-nodes-base.mySql",
"typeVersion": 2.5,
"position": [-880, 900],
"parameters": { "operation": "executeQuery", "query": "={{ $json.query }}", "options": {} }
},
{
"id": "code-normalizar-candidatos-base",
"name": "Code - Normalizar Candidatos Base",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [-640, -160],
"parameters": { "jsCode": "__BUILD__:normalize-candidates.node.js" }
},
{
"id": "code-montar-sql-contexto",
"name": "Code - Montar SQL Contexto",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [-400, -160],
"parameters": { "jsCode": "__BUILD__:build-sql-context.node.js" }
},
{
"id": "mysql-buscar-contexto",
"name": "MySQL - Buscar Contexto",
"type": "n8n-nodes-base.mySql",
"typeVersion": 2.5,
"position": [-160, -160],
"parameters": { "operation": "executeQuery", "query": "={{ $json.query }}", "options": {} }
},
{
"id": "code-mesclar-contexto-hash",
"name": "Code - Mesclar Contexto e Calcular Hash",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [80, -160],
"parameters": { "jsCode": "__BUILD__:merge-context-hash.node.js" }
},
{
"id": "datatable-buscar-memoria",
"name": "Data Table - Buscar Memoria",
"type": "n8n-nodes-base.dataTable",
"typeVersion": 1.1,
"position": [-1120, 300],
"parameters": {
"resource": "row",
"operation": "get",
"dataTableId": { "mode": "name", "value": "coordenador_glpi_memoria" },
"filters": { "conditions": [] },
"returnAll": true
}
},
{
"id": "merge-aguardar-memoria",
"name": "Merge - Aguardar Memoria",
"type": "n8n-nodes-base.merge",
"typeVersion": 3.2,
"position": [320, -160],
"parameters": { "mode": "append", "numberInputs": 2 }
},
{
"id": "code-decidir-necessidade-ia",
"name": "Code - Decidir Necessidade de IA",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [560, -160],
"parameters": { "jsCode": "__BUILD__:decide-needs-ai.node.js" }
},
{
"id": "if-precisa-ia",
"name": "IF - Precisa IA?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [800, -160],
"parameters": {
"conditions": {
"options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 2 },
"conditions": [
{
"id": "cond-precisa-ia",
"leftValue": "={{ $json.precisa_ia }}",
"rightValue": true,
"operator": { "type": "boolean", "operation": "true", "singleValue": true }
}
],
"combinator": "and"
},
"options": {}
}
},
{
"id": "if-ia-ligada",
"name": "IF - IA Ligada?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [1040, -320],
"parameters": {
"conditions": {
"options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 2 },
"conditions": [
{
"id": "cond-ia-ligada",
"leftValue": "={{ $json.config.aiEnabled }}",
"rightValue": true,
"operator": { "type": "boolean", "operation": "true", "singleValue": true }
}
],
"combinator": "and"
},
"options": {}
}
},
{
"id": "code-preparar-prompt-ia",
"name": "Code - Preparar Prompt IA",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [1280, -400],
"parameters": { "jsCode": "__BUILD__:prepare-ai-prompt.node.js" }
},
{
"id": "http-openai-classificar",
"name": "HTTP - OpenAI Classificar Chamado",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.4,
"position": [1520, -400],
"continueOnFail": true,
"parameters": {
"method": "POST",
"url": "https://api.openai.com/v1/chat/completions",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "openAiApi",
"sendBody": true,
"contentType": "json",
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify($json.requestBody) }}",
"options": { "timeout": 30000 }
},
"credentials": { "openAiApi": { "id": "WUGriAn5FgCiLza2", "name": "OpenAI - Coordenador GLPI" } }
},
{
"id": "code-validar-classificacao-ia",
"name": "Code - Validar Classificacao IA",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [1760, -400],
"parameters": { "jsCode": "__BUILD__:validate-ai-response.node.js" }
},
{
"id": "code-fallback-ia-desligada",
"name": "Code - Fallback IA Desligada",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [1280, -160],
"parameters": { "jsCode": "__BUILD__:ai-disabled-fallback.node.js" }
},
{
"id": "code-reusar-classificacao-cache",
"name": "Code - Reusar Classificacao em Cache",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [1040, 80],
"parameters": { "jsCode": "__BUILD__:reuse-cached-classification.node.js" }
},
{
"id": "merge-consolidar-classificacoes",
"name": "Merge - Consolidar Classificacoes",
"type": "n8n-nodes-base.merge",
"typeVersion": 3.2,
"position": [2000, -160],
"parameters": { "mode": "append", "numberInputs": 3 }
},
{
"id": "code-calcular-score",
"name": "Code - Calcular Score",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [2240, -160],
"parameters": { "jsCode": "__BUILD__:compute-score.node.js" }
},
{
"id": "code-preparar-registro-memoria-analise",
"name": "Code - Preparar Registro Memoria Analise",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [2480, 160],
"parameters": { "jsCode": "__BUILD__:prepare-memoria-analise-record.node.js" }
},
{
"id": "datatable-upsert-memoria-analise",
"name": "Data Table - Upsert Memoria Analise",
"type": "n8n-nodes-base.dataTable",
"typeVersion": 1.1,
"position": [2720, 160],
"parameters": {
"resource": "row",
"operation": "upsert",
"dataTableId": { "mode": "name", "value": "coordenador_glpi_memoria" },
"filters": {
"conditions": [
{ "keyName": "ticket_id", "condition": "eq", "keyValue": "={{ $json.ticket_id }}" }
]
},
"columns": {
"mappingMode": "defineBelow",
"matchingColumns": ["ticket_id"],
"value": {
"ticket_id": "={{ $json.ticket_id }}",
"last_analyzed_at": "={{ $json.last_analyzed_at }}",
"last_context_hash": "={{ $json.last_context_hash }}",
"last_meaningful_change_at": "={{ $json.last_meaningful_change_at }}",
"ai_classification": "={{ $json.ai_classification }}",
"ai_confidence": "={{ $json.ai_confidence }}",
"blocker_type": "={{ $json.blocker_type }}",
"waiting_for": "={{ $json.waiting_for }}",
"next_action": "={{ $json.next_action }}",
"next_review_at": "={{ $json.next_review_at }}",
"last_score": "={{ $json.last_score }}",
"last_ai_reason": "={{ $json.last_ai_reason }}",
"updated_at": "={{ $json.updated_at }}"
}
}
}
},
{
"id": "code-selecionar-top10",
"name": "Code - Selecionar Top 10",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [2480, -320],
"parameters": { "jsCode": "__BUILD__:select-top10.node.js" }
},
{
"id": "code-preparar-registro-envio",
"name": "Code - Preparar Registro de Envio",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [2720, -560],
"parameters": { "jsCode": "__BUILD__:prepare-envio-record.node.js" }
},
{
"id": "datatable-registrar-envio",
"name": "Data Table - Registrar Envio",
"type": "n8n-nodes-base.dataTable",
"typeVersion": 1.1,
"position": [2960, -560],
"parameters": {
"resource": "row",
"operation": "upsert",
"dataTableId": { "mode": "name", "value": "coordenador_glpi_memoria" },
"filters": {
"conditions": [
{ "keyName": "ticket_id", "condition": "eq", "keyValue": "={{ $json.ticket_id }}" }
]
},
"columns": {
"mappingMode": "defineBelow",
"matchingColumns": ["ticket_id"],
"value": {
"ticket_id": "={{ $json.ticket_id }}",
"last_sent_at": "={{ $json.last_sent_at }}",
"times_sent": "={{ $json.times_sent }}",
"times_sent_30d": "={{ $json.times_sent_30d }}",
"consecutive_days_sent": "={{ $json.consecutive_days_sent }}",
"updated_at": "={{ $json.updated_at }}"
}
}
}
},
{
"id": "code-montar-mensagem-curta",
"name": "Code - Montar Mensagem Curta",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [2720, -320],
"parameters": { "jsCode": "__BUILD__:build-top-message.node.js" }
},
{
"id": "code-montar-digest-coordenacao",
"name": "Code - Montar Digest Coordenacao",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [2960, -320],
"parameters": { "jsCode": "__BUILD__:build-coordination-digest.node.js" }
},
{
"id": "merge-unir-shadow",
"name": "Merge - Unir Shadow",
"type": "n8n-nodes-base.merge",
"typeVersion": 3.2,
"position": [3200, 160],
"parameters": { "mode": "append", "numberInputs": 2 }
},
{
"id": "code-comparar-legacy-v2",
"name": "Code - Comparar Legacy x V2",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [3440, 160],
"parameters": { "jsCode": "__BUILD__:compare-legacy-v2.node.js" }
},
{
"id": "datatable-registrar-shadow-run",
"name": "Data Table - Registrar Shadow Run",
"type": "n8n-nodes-base.dataTable",
"typeVersion": 1.1,
"position": [3680, 160],
"parameters": {
"resource": "row",
"operation": "insert",
"dataTableId": { "mode": "name", "value": "coordenador_glpi_shadow_runs" },
"columns": {
"mappingMode": "defineBelow",
"value": {
"run_at": "={{ $json.run_at }}",
"legacy_ids": "={{ $json.legacy_ids }}",
"v2_ids": "={{ $json.v2_ids }}",
"added": "={{ $json.added }}",
"removed": "={{ $json.removed }}",
"summary": "={{ $json.summary }}"
}
}
}
},
{
"id": "debug-ver-painel",
"name": "Debug - Ver Painel",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2.1,
"position": [-1600, 1200],
"parameters": {
"httpMethod": "GET",
"path": "coordenador-glpi-v2-debug",
"responseMode": "responseNode",
"options": {}
}
},
{
"id": "datatable-buscar-shadow-runs-recentes",
"name": "Data Table - Buscar Shadow Runs Recentes",
"type": "n8n-nodes-base.dataTable",
"typeVersion": 1.1,
"position": [-1360, 1200],
"parameters": {
"resource": "row",
"operation": "get",
"dataTableId": { "mode": "name", "value": "coordenador_glpi_shadow_runs" },
"filters": { "conditions": [] },
"returnAll": true
}
},
{
"id": "code-montar-painel-debug",
"name": "Code - Montar Painel Debug",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [-1120, 1200],
"parameters": { "jsCode": "__BUILD__:debug-panel.node.js" }
},
{
"id": "debug-responder",
"name": "Debug - Responder",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.5,
"position": [-880, 1200],
"parameters": { "respondWith": "json", "responseBody": "={{ $json }}", "options": {} }
}
],
"connections": {
"Manual Trigger": {
"main": [[{ "node": "Config - Coordenador GLPI v2", "type": "main", "index": 0 }]]
},
"Schedule Trigger - Dias uteis 08h (desligado em DEV)": {
"main": [[{ "node": "Config - Coordenador GLPI v2", "type": "main", "index": 0 }]]
},
"Config - Coordenador GLPI v2": {
"main": [[
{ "node": "Code - Montar SQL Candidatos", "type": "main", "index": 0 },
{ "node": "Code - Montar SQL Legado", "type": "main", "index": 0 },
{ "node": "Data Table - Buscar Memoria", "type": "main", "index": 0 }
]]
},
"Code - Montar SQL Candidatos": {
"main": [[{ "node": "MySQL - Buscar Candidatos", "type": "main", "index": 0 }]]
},
"Code - Montar SQL Legado": {
"main": [[{ "node": "MySQL - Legado Top 10 (Shadow)", "type": "main", "index": 0 }]]
},
"MySQL - Buscar Candidatos": {
"main": [[{ "node": "Code - Normalizar Candidatos Base", "type": "main", "index": 0 }]]
},
"Code - Normalizar Candidatos Base": {
"main": [[{ "node": "Code - Montar SQL Contexto", "type": "main", "index": 0 }]]
},
"Code - Montar SQL Contexto": {
"main": [[{ "node": "MySQL - Buscar Contexto", "type": "main", "index": 0 }]]
},
"MySQL - Buscar Contexto": {
"main": [[{ "node": "Code - Mesclar Contexto e Calcular Hash", "type": "main", "index": 0 }]]
},
"Code - Mesclar Contexto e Calcular Hash": {
"main": [[{ "node": "Merge - Aguardar Memoria", "type": "main", "index": 0 }]]
},
"Data Table - Buscar Memoria": {
"main": [[{ "node": "Merge - Aguardar Memoria", "type": "main", "index": 1 }]]
},
"Merge - Aguardar Memoria": {
"main": [[{ "node": "Code - Decidir Necessidade de IA", "type": "main", "index": 0 }]]
},
"Code - Decidir Necessidade de IA": {
"main": [[{ "node": "IF - Precisa IA?", "type": "main", "index": 0 }]]
},
"IF - Precisa IA?": {
"main": [
[{ "node": "IF - IA Ligada?", "type": "main", "index": 0 }],
[{ "node": "Code - Reusar Classificacao em Cache", "type": "main", "index": 0 }]
]
},
"IF - IA Ligada?": {
"main": [
[{ "node": "Code - Preparar Prompt IA", "type": "main", "index": 0 }],
[{ "node": "Code - Fallback IA Desligada", "type": "main", "index": 0 }]
]
},
"Code - Preparar Prompt IA": {
"main": [[{ "node": "HTTP - OpenAI Classificar Chamado", "type": "main", "index": 0 }]]
},
"HTTP - OpenAI Classificar Chamado": {
"main": [[{ "node": "Code - Validar Classificacao IA", "type": "main", "index": 0 }]]
},
"Code - Validar Classificacao IA": {
"main": [[{ "node": "Merge - Consolidar Classificacoes", "type": "main", "index": 0 }]]
},
"Code - Fallback IA Desligada": {
"main": [[{ "node": "Merge - Consolidar Classificacoes", "type": "main", "index": 1 }]]
},
"Code - Reusar Classificacao em Cache": {
"main": [[{ "node": "Merge - Consolidar Classificacoes", "type": "main", "index": 2 }]]
},
"Merge - Consolidar Classificacoes": {
"main": [[{ "node": "Code - Calcular Score", "type": "main", "index": 0 }]]
},
"Code - Calcular Score": {
"main": [[
{ "node": "Code - Selecionar Top 10", "type": "main", "index": 0 },
{ "node": "Code - Preparar Registro Memoria Analise", "type": "main", "index": 0 }
]]
},
"Code - Preparar Registro Memoria Analise": {
"main": [[{ "node": "Data Table - Upsert Memoria Analise", "type": "main", "index": 0 }]]
},
"Code - Selecionar Top 10": {
"main": [[
{ "node": "Code - Montar Mensagem Curta", "type": "main", "index": 0 },
{ "node": "Code - Preparar Registro de Envio", "type": "main", "index": 0 }
]]
},
"Code - Preparar Registro de Envio": {
"main": [[{ "node": "Data Table - Registrar Envio", "type": "main", "index": 0 }]]
},
"Code - Montar Mensagem Curta": {
"main": [[{ "node": "Code - Montar Digest Coordenacao", "type": "main", "index": 0 }]]
},
"Code - Montar Digest Coordenacao": {
"main": [[{ "node": "Merge - Unir Shadow", "type": "main", "index": 0 }]]
},
"MySQL - Legado Top 10 (Shadow)": {
"main": [[{ "node": "Merge - Unir Shadow", "type": "main", "index": 1 }]]
},
"Merge - Unir Shadow": {
"main": [[{ "node": "Code - Comparar Legacy x V2", "type": "main", "index": 0 }]]
},
"Code - Comparar Legacy x V2": {
"main": [[{ "node": "Data Table - Registrar Shadow Run", "type": "main", "index": 0 }]]
},
"Debug - Ver Painel": {
"main": [[{ "node": "Data Table - Buscar Shadow Runs Recentes", "type": "main", "index": 0 }]]
},
"Data Table - Buscar Shadow Runs Recentes": {
"main": [[{ "node": "Code - Montar Painel Debug", "type": "main", "index": 0 }]]
},
"Code - Montar Painel Debug": {
"main": [[{ "node": "Debug - Responder", "type": "main", "index": 0 }]]
}
},
"pinData": {},
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "",
"meta": {
"templateCredsSetupCompleted": false
},
"tags": []
}