DAP reviewer assignment: align the trigger goal with what the Recommend Reviewers flow expects

Phase A. Blocks the removal of the project setting.

Problem — investigation now complete, mismatch confirmed

The two paths send different goals, and the flow config proves the goal must be the iid:

  • Bespoke path (ee/app/services/ai/duo_workflows/recommend_reviewers/execute_service.rb): goal: merge_request.iid.to_s.
  • Trigger path (Ai::FlowTriggers::RunService#catalog_item_user_prompt, run_service.rb:382-402): recommend_reviewers/v1 declares no goal_templates and the event is not mention, so it falls to the generic foundational-flow branch — Gitlab::UrlBuilder.build(@resource), the MR URL.
  • The flow config (duo_workflow_service/agent_platform/v1/flows/configs/recommend_reviewers/1.0.0.yml in gitlab-org/gitlab-ai-gateway) binds from: "context:goal", as: "merge_request_iid" in both the post_recommendation and assign_reviewers components. The flow reads only context:goal, context:project_id, and context:inputs.reviewer_data.
  • The merge_request_id: resource.iid that RunService#start_catalog_workflow passes separately (run_service.rb:241) is not consumed by the flow config — it never reaches a component input.

So a trigger-driven run feeds a URL into a prompt variable named merge_request_iid. The model may salvage the iid from the URL string, but the create_merge_request_note / update_merge_request tool calls are then relying on LLM parsing where the bespoke path passed exact data. This needs fixing, not documenting.

Implementation

Add a goal template class — the same shape as the two existing single-goal templates (ee/app/models/ai/catalog/goal_templates/security_review.rb, resolve_dependency_bump.rb):

# ee/app/models/ai/catalog/goal_templates/recommend_reviewers.rb
module Ai
  module Catalog
    module GoalTemplates
      class RecommendReviewers < Base
        def self.resolve(event_type:, resource:, user_input: nil, params: {})
          raise ArgumentError, 'resource must not be nil' unless resource

          # The recommend_reviewers flow binds context:goal as merge_request_iid
          # in every component, so the goal must be the bare iid for all
          # trigger event types.
          resource.iid.to_s
        end
      end
    end
  end
end

Declare it on the item next to the existing additional_context_resolver (ee/app/models/ai/catalog/foundational_flow/items.rb:187-205):

goal_templates: ::Ai::Catalog::GoalTemplates::RecommendReviewers,

Notes validated against the code:

  • goal_templates is a plain attribute with no validator (attributes.rb:85-86) — no registration needed anywhere else.
  • With goal_templates present, catalog_item_user_prompt takes the template branch for all event types including mention (run_service.rb:385-391). Returning the iid for mention too is correct for this flow — it has no conversational path; every component only wants the iid. Do not return user_input (for mention events that carries conversation context, not the iid).
  • For merge_request_ready, params is only { input: resource.iid.to_s, event: :merge_request_ready } (ee/app/workers/concerns/ai/cloud_events_flow_trigger_worker.rb:75) — the template must derive everything from resource, which the sketch above does.
  • Precedent for the dispatcher pattern exists if per-event goals are ever needed (goal_templates/developer.rb:7-38), but is not needed here.

Specs:

  • ee/spec/models/ai/catalog/goal_templates/recommend_reviewers_spec.rb — pattern: resolve_dependency_bump_spec.rb (nil resource raises, iid returned for MR, event-type-agnostic).
  • ee/spec/models/ai/catalog/foundational_flow_spec.rb — item declaration assertion (pattern at line 419: expect(flow.goal_templates).to eq(...)).
  • ee/spec/services/ai/flow_triggers/run_service_spec.rbdescribe '#catalog_item_user_prompt' contexts at lines 1584-1730 already cover both the no-template fallback and the goal_templates branch; add a recommend_reviewers context.

Acceptance criteria

  • Documented which input the flow actually reads — context:goal bound as merge_request_iid; the separate merge_request_id param is not consumed
  • recommend_reviewers/v1 declares goal_templates: ::Ai::Catalog::GoalTemplates::RecommendReviewers and trigger-driven runs send the bare iid as the goal
  • Trigger-driven runs reach the same code path in the flow as bespoke runs
  • No change required in the flow config (gitlab-org/gitlab-ai-gateway) — confirmed by reading recommend_reviewers/1.0.0.yml
Edited by 🤖 GitLab Bot 🤖