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/v1declares nogoal_templatesand the event is notmention, 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.ymlingitlab-org/gitlab-ai-gateway) bindsfrom: "context:goal", as: "merge_request_iid"in both thepost_recommendationandassign_reviewerscomponents. The flow reads onlycontext:goal,context:project_id, andcontext:inputs.reviewer_data. - The
merge_request_id: resource.iidthatRunService#start_catalog_workflowpasses 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
endDeclare 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_templatesis a plain attribute with no validator (attributes.rb:85-86) — no registration needed anywhere else.- With
goal_templatespresent,catalog_item_user_prompttakes the template branch for all event types includingmention(run_service.rb:385-391). Returning the iid formentiontoo is correct for this flow — it has no conversational path; every component only wants the iid. Do not returnuser_input(for mention events that carries conversation context, not the iid). - For
merge_request_ready,paramsis 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 fromresource, 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.rb—describe '#catalog_item_user_prompt'contexts at lines 1584-1730 already cover both the no-template fallback and thegoal_templatesbranch; add a recommend_reviewers context.
Acceptance criteria
- Documented which input the flow actually reads —
context:goalbound asmerge_request_iid; the separatemerge_request_idparam is not consumed -
recommend_reviewers/v1declaresgoal_templates: ::Ai::Catalog::GoalTemplates::RecommendReviewersand 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 readingrecommend_reviewers/1.0.0.yml