Class: Collavre::Comment

Inherits:
ApplicationRecord show all
Includes:
Approvable, Broadcastable, ClaudeChannelPermission, Notifiable
Defined in:
app/models/collavre/comment.rb,
app/models/collavre/comment/approvable.rb,
app/models/collavre/comment/notifiable.rb,
app/models/collavre/comment/broadcastable.rb,
app/models/collavre/comment/claude_channel_permission.rb

Defined Under Namespace

Modules: Approvable, Broadcastable, ClaudeChannelPermission, Notifiable

Constant Summary collapse

STREAMING_PLACEHOLDER_CONTENT =
"..."
WAITING_NOTICE_PREFIX =

Authorless “⏳” waiting-notice system messages posted when an agent is deferred for topic concurrency. AgentOrchestrator.cleanup_waiting_notices! matches the same prefix to remove them once the waiter is dequeued.

""

Constants included from ClaudeChannelPermission

ClaudeChannelPermission::ACTION_TYPE

Constants included from Broadcastable

Broadcastable::INBOX_BADGE_TARGETS

Instance Method Summary collapse

Methods included from ClaudeChannelPermission

#broadcast_claude_channel_permission_decision, #claude_channel_permission?, #claude_channel_permission_denied?, #claude_channel_permission_request_id, #decide_claude_channel_permission!, #rebroadcast_claude_channel_permission_decision

Methods included from Approvable

#approval_status, #can_be_approved_by?, #parsed_action_tool_name

Methods included from Notifiable

#mentioned_users, #notify_ai_completion

Instance Method Details

#creative_snippetObject

public for db migration



122
123
124
# File 'app/models/collavre/comment.rb', line 122

def creative_snippet
  creative.creative_snippet
end

#dispatch_payloadObject

Build the dispatch payload for comment_created events. Used by both after_create_commit callback and DropTriggerJob to ensure a single source of truth (no payload drift).



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'app/models/collavre/comment.rb', line 129

def dispatch_payload
  {
    comment: {
      id: id,
      content: content,
      user_id: user_id,
      from_ai: user&.searchable? || false,
      quoted_comment_id: quoted_comment_id
    }.compact,
    creative: {
      id: creative_id,
      description: creative&.description
    },
    topic: {
      id: topic_id
    },
    chat: {
      content: content
    }
  }
end

#next_version_numberObject



113
114
115
# File 'app/models/collavre/comment.rb', line 113

def next_version_number
  (comment_versions.maximum(:version_number) || 0) + 1
end

#review_message?Boolean

Returns:

  • (Boolean)


117
118
119
# File 'app/models/collavre/comment.rb', line 117

def review_message?
  quoted_comment_id.present? && !review_type_question?
end

#to_partial_pathObject

Use non-namespaced partial path for backward compatibility



12
13
14
# File 'app/models/collavre/comment.rb', line 12

def to_partial_path
  "comments/comment"
end

#topic_blocking_taskObject

The task holding this topic’s concurrency slot — the blocker this waiting notice is about. Lets the notice render a stop button that cancels the blocker (freeing the topic so the deferred waiter proceeds) instead of being an anonymous dead end. Resolved at render time rather than stored on task_id, which Task#reply_comment keys on (a shared task_id would make the blocker’s reply_comment ambiguous).

Two gates keep the button honest:

1. Only THIS notice's own topic-concurrency defer qualifies. The same "⏳"
   notice is also posted for :delayed decisions (busy / rate_limited),
   which schedule a delayed job WITHOUT queuing a topic waiter —
   cancelling some unrelated running task would not unblock them.
   topic_concurrency_defer is set only on the :deferred path, so a
   :delayed notice never shows the button even when an unrelated queued
   waiter happens to share the topic. The queued_for_topic check then
   confirms a waiter is still actually pending on the slot.
2. Resolve the blocker over occupying_topic_slot, not just running/
   delegated: a holder paused on pending_approval still occupies the slot
   and is cancellable, so the button must stay visible for it.

Returns nil once no slot holder remains — at which point the notice itself is cleaned up.



43
44
45
46
47
48
49
50
51
52
# File 'app/models/collavre/comment.rb', line 43

def topic_blocking_task
  return @topic_blocking_task if defined?(@topic_blocking_task)

  @topic_blocking_task =
    if topic_concurrency_defer? && topic_id &&
       Collavre::Task.queued_for_topic(topic_id, creative_id).exists?
      Collavre::Task.occupying_topic_slot(topic_id, creative_id)
                    .includes(:agent).order(:created_at).first
    end
end

#waiting_notice?Boolean

A system “⏳” waiting notice (no author) telling a user their agent is deferred because another task holds the topic’s running slot.

Returns:

  • (Boolean)


18
19
20
# File 'app/models/collavre/comment.rb', line 18

def waiting_notice?
  user_id.nil? && content.to_s.start_with?(WAITING_NOTICE_PREFIX)
end