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, while RunService (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. TriggerFlowService didn't check Ability.allowed?(:execute_ai_catalog_item), so the Slack path skipped permission checks that RunService enforced.
  • 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 to TriggerFlowService. 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_context didn't include service_account_id or flow_reference.
  • No error resilience in adapter lifecycle hooks. If a hook like on_flow_started raised, 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

How to set up and validate locally

The safest validation is to confirm existing flows still work:

  1. In your GDK, set up a project with Duo Developer enabled
  2. Create an issue and assign Duo Developer — verify the processing note appears and a workflow starts
  3. Mention @duo-developer in 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.rb

MR 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.

Merge request reports

Loading