Corrige bug real de idempotencia: message_hash nao deve fazer parte do send_key

Bug encontrado AO VIVO durante o teste controlado de envio (secao 14): a
IA (gpt-5.5) nao e deterministica. Duas chamadas para o MESMO periodo
(mesmo report_type+data) geraram insights com textos diferentes, portanto
message_hash diferente, portanto send_key diferente — o retry do MESMO
periodo passou pela deteccao de duplicata como se fosse outro relatorio.

Resultado real: 3 mensagens verdadeiras enviadas ao canal C0BJNBTPFT5
para o periodo 'diario 2026-08-24' antes de eu interromper e corrigir
(execucoes 16744, 16746 e uma terceira ja em voo quando a guarda foi
revertida). Estado revertido imediatamente (DEV_MODE/SHADOW_MODE=true,
workflow desativado) assim que o padrao foi identificado.

Correcao: src/v2/sendGuard.js#buildSendKey agora usa so
report_type+period_start+period_end+destination (SEM message_hash) — a
identidade de um relatorio e o PERIODO, nao o texto exato produzido pela
IA. message_hash continua calculado e armazenado, so para diagnostico.

As 3 linhas reais gravadas na Data Table dev_glpi_v2_send_log foram
normalizadas manualmente (send_key sem sufixo de hash) para que a
protecao de duplicata funcione corretamente para qualquer nova tentativa
do mesmo periodo daqui pra frente.

Testes novos: sendGuard.test.js e pipeline.test.js ganharam testes de
regressao reproduzindo exatamente este cenario (retry do mesmo periodo
com aiCall/mensagem diferente -> send_key igual -> duplicata bloqueada).

Canal Slack real confirmado nesta sessao: D06JK4GEK8E (DM) foi tentado
primeiro e falhou com channel_not_found (bot sem acesso); o usuario optou
por reaproveitar C0BJNBTPFT5 (canal do workflow irmao Coordenador GLPI,
onde o bot ja e membro).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Automacao GLPI
2026-08-25 09:44:44 -03:00
co-authored by Claude Sonnet 5
parent f6f12c53d8
commit 9475e68cb1
6 changed files with 79 additions and 17 deletions
+6 -5
View File
@@ -84,11 +84,12 @@ module.exports = function build(codeBodies) {
setAssignment('FORCE_SEND', 'FORCE_SEND', 'boolean', false),
// Canal Slack de destino do Resumo Diario/Semanal. Confirmado
// explicitamente pelo usuario em 2026-08-25 (ver secao 6 da
// missao — nao foi adivinhado). D06JK4GEK8E: prefixo "D" indica
// uma conversa direta (DM)/IM, nao um canal publico/privado
// ("C"/"G") — comportamento identico via chat.postMessage, so
// registrando aqui para quem for auditar depois.
setAssignment('SLACK_DESTINATION', 'SLACK_DESTINATION', 'string', 'D06JK4GEK8E'),
// missao — nao foi adivinhado). D06JK4GEK8E (DM) foi tentado
// primeiro e falhou ao vivo com "channel_not_found" (o bot nao
// tem acesso); o usuario optou por reaproveitar o MESMO canal
// ja usado pelo workflow irmao "Coordenador GLPI" (C0BJNBTPFT5),
// onde o bot ja e membro confirmado.
setAssignment('SLACK_DESTINATION', 'SLACK_DESTINATION', 'string', 'C0BJNBTPFT5'),
setAssignment('EXTENDED_METRICS_ENABLED', 'EXTENDED_METRICS_ENABLED', 'boolean', false),
setAssignment('ANOMALY_MULTIPLIER', 'ANOMALY_MULTIPLIER', 'number', 2.0),
setAssignment('ANOMALY_MIN_DELTA', 'ANOMALY_MIN_DELTA', 'number', 3),