Return 400 for invalid code review flow goals in workflows API
What does this MR do and why?
Before this change, POST /api/v4/ai/duo_workflows/workflows could trigger the Duo Code Review flow (code_review/v1) via either ai_catalog_item_consumer_id or the workflow_definition parameter, and the flow resolved its target merge request with find_by_iid(goal). Any goal string was accepted, so a free-text goal (prose instead of an identifier) still started a session: all LLM steps ran and were billed, only the final publish step failed, nothing was posted to the MR, the caller never learned why, and the free-text goal reached the LLM context verbatim.
This MR validates the goal up front, on both code paths, whenever the requested flow is code review: the goal must be a bare MR IID or the URL of an existing MR in the same project, otherwise the API returns 400 before any session is created or billed. URL goals are normalized to the bare IID before forwarding, since the downstream service expects an IID.
On the consumer path, the flow is identified from consumer.item.foundational_flow_reference rather than the params-first resolve_workflow_definition helper, since that helper would let a caller bypass the guard by sending a different workflow_definition alongside a code-review consumer. Request specs cover both paths: accepted IID, normalized same-project URL, and 400 for free text, non-existent MRs, and cross-project URLs.
Also documents the API way of triggering the flow under Code Review Flow > Use the flow, which previously stated the flow was available in the UI only.
How to set up and validate locally
-
On a GDK instance with Duo Code Review available, find a project with the Code Review flow enabled (an
Ai::Catalog::ItemConsumerforcode_review/v1), or useworkflow_definition: code_review/v1directly. -
POST to the endpoint with an invalid goal and expect 400:
curl -X POST "http://gdk.test:3000/api/v4/ai/duo_workflows/workflows" \ --header "PRIVATE-TOKEN: $GDK_PAT" --header "Content-Type: application/json" \ --data '{"project_id": <project-id>, "ai_catalog_item_consumer_id": <consumer-id>, "goal": "This is not a merge request", "start_workflow": true, "environment": "ambient"}' -
Repeat with
"goal": "<mr-iid>"and expect 201 (session created and review runs end to end). -
Repeat with the full URL of the same merge request and expect 201, with the created session's goal normalized to the bare IID.
Screenshots
Correct payload
Incorrect payload
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.
References
Related to #610993 (closed)
