From e662913b2198c396f794b859619a96e6773b430e Mon Sep 17 00:00:00 2001 From: Rodribm10 Date: Sat, 2 May 2026 08:32:47 -0300 Subject: [PATCH] =?UTF-8?q?fix(captain/hermes):=20auto-react=20idempotente?= =?UTF-8?q?=20=E2=80=94=20bloqueia=20retry=20duplicate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OutgoingJob faz retry no DispatchError (até 3x ActiveJob + Sidekiq). Cada retry chamava AutoReactService.maybe_react! e criava uma reaction nova — observado em prod 02/05 quando o env var CAPTAIN_HERMES_SUBSCRIPTION_SECRET_INBOX_5 estava faltando, gerando 401 → 6 reações duplicadas no inbox EXPRESS. Adiciona guard already_reacted? que checa se já existe Message outgoing com external_source='hermes_auto_react' e in_reply_to=msg.id antes de criar uma nova. Defesa contra futuro 5xx/timeout do Hermes daemon. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../app/services/captain/hermes/auto_react_service.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/enterprise/app/services/captain/hermes/auto_react_service.rb b/enterprise/app/services/captain/hermes/auto_react_service.rb index dd693d4ac..5a998682f 100644 --- a/enterprise/app/services/captain/hermes/auto_react_service.rb +++ b/enterprise/app/services/captain/hermes/auto_react_service.rb @@ -33,6 +33,7 @@ class Captain::Hermes::AutoReactService def maybe_react! return unless eligible? + return if already_reacted? emoji = decide_emoji return if emoji.blank? @@ -53,6 +54,16 @@ class Captain::Hermes::AutoReactService true end + # Evita reaction duplicada quando OutgoingJob retentar (ex: dispatch + # retornou 401/5xx e Sidekiq reenfileirou). Sem essa guarda, cada retry + # cria uma reaction nova e cliente vê N emojis seguidos. + def already_reacted? + @conversation.messages + .where(message_type: :outgoing) + .where("content_attributes ->> 'external_source' = ?", 'hermes_auto_react') + .exists?(["(content_attributes ->> 'in_reply_to')::int = ?", @message.id]) + end + def decide_emoji text = @message.content.to_s.strip