Duo Messaging: orchestrator, adapter base, and callback infrastructure
## Problem The [Duo Messaging Service architecture (ADR 008)](https://gitlab.com/gitlab-com/content-sites/handbook/-/merge_requests/19020) defines an adapter pattern for triggering agent flows from external messaging services (Slack, Teams, etc.) and delivering results back asynchronously. The foundational workspace project and service account services are being built in a separate issue, but the core orchestration — resolving a namespace, triggering a flow, and delivering results via EventStore callbacks — does not exist yet. Without this infrastructure, there is no way to trigger an agent flow from a messaging context and get results back. ## Proposal Implement the messaging service core under `Ai::Messaging`: ### `TriggerFlowService` The orchestrator that ties everything together: 1. Resolves the user's `duo_default_namespace` (existing user preference) 2. **Validates that `developer/v1` is enabled** for the namespace (early, actionable error if not) 3. Finds or creates the `duo-workspace` project (via `WorkspaceProjectService` from !232332) 4. Resolves the service account via `Ai::Catalog::ItemConsumers::ResolveServiceAccountService` (reuses the catalog SA — see [design decision](#design-decisions) below) 5. Ensures the service account has access to the workspace project 6. Creates a workflow record with `messaging_callback_context` (JSONB) 7. Starts the agent flow via the existing `StartWorkflowService` Uses `Ai::Catalog::FoundationalFlow['developer/v1']` to source agent privileges, environment, and the catalog item reference from the canonical flow definition. ### `Adapters::Base` Abstract base class defining the adapter interface: - Required: `deliver_result`, `deliver_error` - Optional lifecycle hooks with no-op defaults: `on_flow_started`, `on_flow_completed`, `on_flow_failed` ### `CallbackWorker` An EventStore subscriber that listens for `WorkloadFinishedEvent`: - Loads the workflow via the workload association - Checks for `messaging_callback_context` — skips if absent (not a messaging-triggered workflow) - Resolves the adapter from the callback context via `ADAPTER_REGISTRY` - Extracts the final message from workflow checkpoints - Calls `deliver_result` or `on_flow_failed` on the adapter > **Note:** The `ADAPTER_REGISTRY` will be empty (`{}`) in this MR. Concrete adapters (Slack, etc.) are added in subsequent issues (#597571, #597572). The worker safely no-ops when no adapter matches. ### Migration Adds a `messaging_callback_context` JSONB column to the `duo_workflows_workflows` table. This column stores adapter-specific delivery information (e.g., Slack team ID, channel ID, thread timestamp). ### Scope - `ee/app/services/ai/messaging/trigger_flow_service.rb` + spec - `ee/app/services/ai/messaging/adapters/base.rb` + spec - `ee/app/workers/ai/messaging/callback_worker.rb` + spec - `ee/lib/gitlab/event_store/subscriptions/ai_subscriptions.rb` (EventStore wiring) - `config/sidekiq_queues.yml` (new queue) - `ee/app/workers/all_queues.yml` - DB migration for `messaging_callback_context` No feature flag needed — these components are inert without an entry point calling them. ### Design decisions #### Reuse catalog service account (not a separate messaging SA) The PoC (!231853) had a dedicated `Ai::Messaging::ResolveServiceAccountService` that created its own service account per namespace. In !232332 (issue https://gitlab.com/gitlab-org/gitlab/-/work_items/597569), we decided that messaging is a _trigger mechanism_ for `developer/v1`, not a separate flow. Therefore: - **`TriggerFlowService` uses `Ai::Catalog::ItemConsumers::ResolveServiceAccountService`** with the `developer/v1` catalog item to resolve the SA that was already created when an admin enabled the flow. - **No `Ai::Messaging::ResolveServiceAccountService`** exists — it was removed from scope. - This means `developer/v1` must be enabled for the namespace before messaging can trigger flows. The service validates this early and returns a `:flow_not_enabled` error reason that adapters can use to give platform-specific guidance (e.g., _"Ask your admin to enable the Developer flow for your group."_). #### Flow enablement check Added an explicit early validation that `developer/v1` is enabled for the resolved namespace. Without this, the failure would surface as a generic "Could not resolve service account" error from the catalog SA lookup — unhelpful for users and adapter error messages. The `:flow_not_enabled` error reason lets each adapter (Slack, Teams, etc.) craft an appropriate user-facing message. ### Reference - PoC implementation: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/231853 - Workspace project service (issue #1): https://gitlab.com/gitlab-org/gitlab/-/merge_requests/232332 ## Related - Parent issue: https://gitlab.com/gitlab-org/gitlab/-/work_items/590434 - ADR: https://gitlab.com/gitlab-com/content-sites/handbook/-/merge_requests/19020 - Blocked by: https://gitlab.com/gitlab-org/gitlab/-/work_items/597569 (workspace project — !232332) - Blocks: https://gitlab.com/gitlab-org/gitlab/-/work_items/597571 (Slack adapter)
task