Extract messaging adapter infrastructure for AI flow triggers
What does this MR do and why?
Refactors the AI flow trigger infrastructure to support a unified adapter pattern where different messaging surfaces (Slack, GitLab notes, future surfaces) can trigger AI flows through a shared code path.
This refactoring is based on learnings from adapter PoCs (!232441 (closed), !234788 (closed)) that surfaced bugs and pain points in the current design:
- Composite identity was missing from the messaging path.
TriggerFlowService(used by Slack) had no composite identity linking, whileRunService(used by mention triggers) did. Flows triggered via Slack would run without composite identity — the service account couldn't act on behalf of the user. - No authorization check in the messaging path.
TriggerFlowServicedidn't checkAbility.allowed?(:execute_ai_catalog_item), so the Slack path skipped permission checks thatRunServiceenforced. - Duplicated orchestration logic drifted. Both services independently resolved
service accounts, checked enablement, and managed workspace projects. When
composite identity was added to
RunService, it wasn't propagated toTriggerFlowService. Any future cross-cutting concern would hit the same problem. - Callback context lacked service account info. The async callback path had
no way to resolve the service account because
callback_contextdidn't includeservice_account_idorflow_reference. - No error resilience in adapter lifecycle hooks. If a hook like
on_flow_startedraised, the entire worker would fail without tracking.
This MR fixes these issues by centralizing shared logic and extending
TriggerFlowService to be the single orchestration primitive for all surfaces.
References
- Epic: #590434
- Slack adapter PoC: !232441 (closed)
- GitLab note adapter PoC: !234788 (closed)
How to set up and validate locally
The safest validation is to confirm existing flows still work:
- In your GDK, set up a project with Duo Developer enabled
- Create an issue and assign Duo Developer — verify the processing note appears and a workflow starts
- Mention
@duo-developerin an issue or MR comment — verify the same behavior
These exercise RunService which now delegates composite identity to
Linker.ensure_linked instead of inline methods. If anything regressed,
the flow would fail to start or run without composite identity.
Specs:
bundle exec rspec \
ee/spec/services/ai/composite_identity/linker_spec.rb \
ee/spec/services/ai/flow_triggers/run_service_spec.rb \
ee/spec/services/ai/messaging/trigger_flow_service_spec.rb \
ee/spec/services/ai/messaging/adapters/base_spec.rb \
ee/spec/workers/ai/messaging/callback_worker_spec.rbMR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.