fix(v2): fix cache is_fallback bug, wire observability + data quality, drop debug webhook, fix GLPI URL

- Real bug found by code review while implementing this cycle: "Code -
  Reusar Classificacao em Cache" always hardcoded is_fallback: false when
  reconstructing a classification from memory, even when the cached
  classification was itself AI_FALLBACK. On a cache hit for a previously
  fallback-classified ticket, this would have let it skip
  applyFallbackPolicy's exclusion entirely (the policy checks
  aiClassification.is_fallback first). Fixed to derive is_fallback from
  the cached ai_classification value.
- select-top10.node.js now carries `resolucao` (ia | cache | fallback_cap)
  through to the scored item, previously dropped -- needed to tell cache
  hits apart from real AI calls in observability.
- Code - Comparar Legacy x V2 now computes the full observability summary
  (candidate_count, cache_hits/misses, ai_requested/success/errors,
  ai_fallback_count and by-reason breakdown, classification_counts,
  insufficient_context_count, actionable_real_count,
  critical_override_count, top10_count, blocked_count,
  coordination_count) and the data-quality review (buildDataQualityReview
  over INSUFFICIENT_CONTEXT tickets), both stored in the shadow_runs
  `summary` JSON column -- no new Data Table needed.
- Removed the debug webhook entirely (Debug - Ver Painel, Data Table -
  Buscar Shadow Runs Recentes debug branch, Code - Montar Painel Debug,
  Debug - Responder) and the now-unused debug-panel.node.js driver.
  Workflow went from 37 to 33 nodes. Debugging goes through n8n execution
  data, the Data Tables, and the shadow diff instead, as requested.
- Fixed glpi_base_url: was still V1's own placeholder
  (https://glpi.suaempresa.com.br, never a real value, not report
  anonymization) -- confirmed the real domain (https://glpi.gtigrupo.com.br)
  from the live, active GLPI x WhatsApp workflow's own GLPI API config in
  the same n8n instance. SQL still only ever receives it through
  {GLPI_BASE_URL} substitution from the Config node, never hardcoded.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Gustavo
2026-08-24 19:50:48 -03:00
co-authored by Claude Sonnet 5
parent 485234dc88
commit 3c87ab55e1
7 changed files with 101 additions and 177 deletions
+27
View File
@@ -47,3 +47,30 @@ test('nenhum jsCode gerado contem "require(" sobrando (modulos devem estar total
}
}
});
test('workflow gerado NAO tem nenhum node de webhook de debug', () => {
const wf = buildWorkflow();
const names = wf.nodes.map((n) => n.name);
assert.ok(!names.includes('Debug - Ver Painel'));
assert.ok(!names.includes('Debug - Responder'));
assert.ok(!names.includes('Code - Montar Painel Debug'));
const webhookNodes = wf.nodes.filter((n) => n.type === 'n8n-nodes-base.webhook');
assert.equal(webhookNodes.length, 0, 'nenhum node webhook deveria sobrar no workflow gerado');
});
test('glpi_base_url no Config nao e mais o placeholder do V1', () => {
const wf = buildWorkflow();
const configNode = wf.nodes.find((n) => n.name === 'Config - Coordenador GLPI v2');
const glpiField = configNode.parameters.assignments.assignments.find((a) => a.name === 'glpi_base_url');
assert.notEqual(glpiField.value, 'https://glpi.suaempresa.com.br');
assert.equal(glpiField.value, 'https://glpi.gtigrupo.com.br');
});
test('link_glpi na query de candidatos usa a variavel de config, nao um dominio fixo no SQL', () => {
const fs = require('node:fs');
const path = require('node:path');
const sql = fs.readFileSync(path.join(__dirname, '..', 'sql', 'candidatos_v2_glpi.sql'), 'utf8');
assert.ok(sql.includes("CONCAT('{GLPI_BASE_URL}'"), 'o SQL deve continuar usando o placeholder substituido pelo node, nunca um dominio hardcoded');
assert.ok(!sql.includes('suaempresa.com.br'));
assert.ok(!sql.includes('gtigrupo.com.br'), 'o dominio real deve vir so da config, nunca hardcoded no SQL');
});