Bail out of a flow trigger run when the flow does not support the resource type
Ai::FlowTriggers::RunService will happily start a flow for a resource type the flow cannot handle. recommend_reviewers/v1 declares no supported_events, so an @duo mention on an issue starts a workflow whose goal is the flow's description sentence bound to merge_request_iid.
!248388 (merged) guarded the goal template and the additional_context_resolver on MergeRequest, which stops the NoMethodError and stops an issue iid being read as a merge request iid - but the run still starts and no-ops. It should not start at all.
Proposal: declare the resource types a foundational flow accepts, and bail in RunService#validation_error when the trigger resource is not one of them.
Detailed context for AI agents
Why it happens
Ai::Catalog::FoundationalFlow has no notion of an accepted resource type. Two existing attributes look adjacent and neither works:
supported_eventsis event-shaped. It isnilforrecommend_reviewers/v1(ee/app/models/ai/catalog/foundational_flow/items.rb), andnilmeans any event is allowed - seeAi::FlowTrigger#supported_events_match_foundational_flow. Narrowing it to merge-request-ish events would be the wrong axis anyway: it is the resource that matters, not the event.preconditionis aGitlab::FilterEvaluatorfilter run against the webhook payload and merged per hook scope (ee/app/services/ai/flow_triggers/filter_evaluator.rb). Expressing "the resource must be aMergeRequest" there means writing one rule per payload shape the flow can receive, and keeping them in sync.
So the check needs a new declaration, e.g. supported_resource_types: [::MergeRequest], defaulting to nil for "no restriction" so existing flows are unaffected.
Where the guard goes
Ai::FlowTriggers::RunService#validation_error - it already returns ServiceResponse.error for non-human users and for third-party permission failures, and it is evaluated inside execute_flow, which is inside the CreateNoteService#execute block. CreateNoteService yields, and on an error response calls mark_failed(note, response.message) and returns the response rather than raising, so the mention loop in Notes::PostProcessService stays intact and sibling triggers are not re-run on NewNoteWorker retry. That is exactly the failure shape wanted here.
Once this lands, the two MergeRequest guards in recommend_reviewers/v1 can be reconsidered. The additional_context_resolver guard also covers the bespoke CreateAndStartWorkflowService path, so it may still be worth keeping; the goal template guard becomes redundant for the trigger path.
Scope note
Adding the attribute changes trigger behaviour for every flow that adopts it, which is why it was left out of !248388 (merged) - that MR's job was making the trigger path work at all. Raised by @kinsingh in review: !248388 (comment 3672261843)
Epic: gitlab-org#23019