From 7a73a687538e06e663b685c56df52da45df9a6eb Mon Sep 17 00:00:00 2001 From: Tanmay Deep Sharma Date: Wed, 15 Jul 2026 12:36:58 +0530 Subject: [PATCH] fix(automations): cancel stale pending runs on delay edit; restrict conversation-level delayed rules to status conditions - Editing a rule's execution_delay (removing or changing it) now cancels any pending executions armed under the old configuration instead of leaving them to fire on a stale schedule. - conversation_created/updated/opened/resolved delayed rules key their episode on status_changed_at alone, so a delayed condition on any other attribute (assignee, team, priority, ...) could collapse distinct qualifying periods into one episode. Restricted to status conditions until episodes track per-attribute change times. --- app/models/automation_rule.rb | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/app/models/automation_rule.rb b/app/models/automation_rule.rb index 9b7d578b7..01ee19dad 100644 --- a/app/models/automation_rule.rb +++ b/app/models/automation_rule.rb @@ -35,8 +35,11 @@ class AutomationRule < ApplicationRecord validates :account_id, presence: true validates :execution_delay, numericality: { only_integer: true, in: EXECUTION_DELAY_RANGE }, allow_nil: true validate :execution_delay_supported_conditions + validate :execution_delay_supported_event after_update_commit :reauthorized!, if: -> { saved_change_to_conditions? } + # Rows already armed under the old delay must not fire on a config the rule no longer has. + after_update :cancel_stale_pending_executions, if: -> { saved_change_to_execution_delay? } scope :active, -> { where(active: true) } @@ -110,6 +113,22 @@ class AutomationRule < ApplicationRecord errors.add(:execution_delay, 'cannot be used with attribute_changed conditions.') end + # Conversation-level events (anything but message_created) key their episode on + # status_changed_at alone. A delayed condition on any other attribute (assignee, team, + # priority, ...) would collapse distinct qualifying periods into one episode and could + # fire on a stale window, so only status conditions are supported until episodes track + # per-attribute change times. + def execution_delay_supported_event + return if execution_delay.blank? || conditions.blank? || event_name == 'message_created' + return if conditions.all? { |obj| obj['attribute_key'] == 'status' } + + errors.add(:execution_delay, 'only supports status conditions for conversation-level events.') + end + + def cancel_stale_pending_executions + pending_executions.pending.find_each { |execution| execution.update!(status: :skipped, skip_reason: 'rule_edited') } + end + def validate_single_condition(condition) query_operator = condition['query_operator']