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.
This commit is contained in:
Tanmay Deep Sharma
2026-07-15 12:36:58 +05:30
parent 55d68d930d
commit 7a73a68753
+19
View File
@@ -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']