Add GitlabNote messaging adapter for @mention-triggered AI flows
What does this MR do and why?
Introduces Ai::Messaging::Adapters::GitlabNote, a concrete adapter implementing the redesigned Adapters::Base contract (from !236207 (merged)) to handle AI flows triggered by @mention in GitLab notes.
GitlabNote adapter
Implements the Base surface contract:
build_callback_context: serializes note/discussion IDs, progress note ID, and service account ID for async deliveryon_request_received: creates a progress note ("🔄 Processing...") with early composite identity linkingon_flow_started: updates the progress note with a workflow linkon_flow_failed: updates the progress note in-place, or falls back todeliver_errordeliver_result/deliver_error: posts reply notes in the original discussion
ConversationContextBuilder
New reusable service (Ai::Notes::ConversationContextBuilder) that extracts discussion history as formatted <message> blocks with note IDs and author attribution. Filters system notes, caps at 15 recent messages. Used by both the adapter path and the RunService path.
Routing and feature flag
PostProcessService routes foundational flow triggers through the adapter when ai_use_messaging_adapter_for_mentions is enabled (project-scoped, gitlab_com_derisk). The caller builds the TriggerBundle (goal, service account, flow reference) following the "caller resolves, adapter delivers" pattern from !236207 (merged).
When the flag is disabled, the legacy RunService path is used. Both paths now provide full conversation context (previously RunService only sent the single triggering note).
The CallbackWorker resolves GitlabNote via the static ADAPTER_REGISTRY.
Supports Issue, MergeRequest, and WorkItem noteables. Epics are out of scope (note.project is nil for epics).
Other changes
- Mention template: uses
<gitlab_context>/<conversation>tags withsource_contextparam - FlowTrigger: adds
foundational_flow?predicate used by routing - RunService path: now receives full conversation context via
ConversationContextBuilder
Depends on
- !236207 (merged) — redesigned
Adapters::Basewith caller-resolves pattern (currently in merge train)
Related issues
How to set up and validate locally
Specs
bundle exec rspec \
ee/spec/services/ai/notes/conversation_context_builder_spec.rb \
ee/spec/services/ai/messaging/adapters/gitlab_note_spec.rb \
ee/spec/services/ee/notes/post_process_service_spec.rb \
ee/spec/workers/ai/messaging/callback_worker_spec.rb \
ee/spec/models/ai/flow_trigger_spec.rb \
ee/spec/models/ai/catalog/goal_templates/developer/mention_spec.rbManual end-to-end testing
Prerequisites
-
Local GDK running with Duo Workflow service connected
-
Developer foundational flow (
developer/v1) enabled for a namespace -
A service account (e.g.
@duo-developer-gitlab-duo) configured with an@mentionflow trigger on a test project -
A test issue or MR in that project
-
Feature flag enabled:
# In bin/rails c Feature.enable(:ai_use_messaging_adapter_for_mentions)
Important: Trigger processing runs in Sidekiq (
NewNoteWorker→PostProcessService), so code patches requiregdk restart rails-background-jobs.
Happy paths
1. Top-level @mention on an Issue
Post @duo-developer-gitlab-duo say hi as a new comment on an issue.
Expected:
- A "
🔄 Processing..." progress note appears as a reply - Progress note updates to "
✅ has started working on this" - Agent response posted as a new note in the same thread
2. @mention in an existing discussion thread
Reply inside an existing thread with @duo-developer-gitlab-duo summarize this discussion.
Expected:
- Progress note and reply appear in the same thread
- In the agent session, the goal contains
<conversation>tags with earlier messages and<gitlab_context>with resource URL/title
Checking adapter state via console
w = Ai::DuoWorkflows::Workflow.where.not(messaging_callback_context: nil).order(id: :desc).first
pp w.messaging_callback_context
# Should include: adapter, project_id, noteable_type, discussion_id, service_account_id, progress_note_id
klass = Ai::Messaging::CallbackWorker::ADAPTER_REGISTRY[w.messaging_callback_context['adapter']]
adapter = klass.from_callback_context(w.messaging_callback_context)MR acceptance checklist
This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.
- I have evaluated the MR acceptance checklist for this MR.