Let code_review/v1 accept a checkout review from Duo CLI
What does this MR do and why?
Duo CLI is adding a local code review that runs the code_review/v1 foundational flow against the developer's checkout, as a headless, client-executed session. The flow's goal validator only accepted a merge request IID or URL (added in this merge request so a bad goal fails before LLM steps are billed), so a checkout run got a 400 at create time.
This merge request:
- Renames the shared
goal_validator_resolverattribute tostart_request_validator, adding two keywords:additional_context:(the API request's additional context; always empty on trigger runs, which build their context after validation) andclient_executed:(truewhen the caller runs the flow itself, mirroringFlowExecutionAuthorizer#client_executed?). The model method is nowvalidate_start_request. The endpoint passesparams[:additional_context];client_executedistrueon the plain create path unlessstart_workflowis set,falseon theai_catalog_item_consumer_idpath, andfalsefrom the trigger service. Shared code stays unaware of code review or surfaces. Onlycode_review/v1andbl_securitydeclare the validator;bl_securityjust gains the unused keywords. - All checkout logic lives in the
code_review/v1definition. It readssurfacefrom thecode_review_contextentry ofadditional_context(Contentis a JSON object with asurfacekey). Forcheckout, it requires client execution and a non-blank (stripped) goal. Otherwise the existing merge request check runs unchanged. Content that is not a JSON object is ignored, so the merge request rule still applies. - Rejections (all
400, before a workflow is created):- Unknown
surface:code_review_context.surface must be one of merge_request, checkout. checkouton a server-started run (start_workflow: trueorai_catalog_item_consumer_id):Checkout reviews run on the client and cannot be started on the server.- Blank checkout goal:
The goal for a checkout review must describe what to review.
- Unknown
No new API parameter, documentation or OpenAPI change, storage change, feature flag, or migration.
Notes on related behavior (unchanged by design):
- Noteable linking:
noteable_resolverstill only matches a bare IID or a same-project merge request URL. A free-text checkout goal resolves to no noteable. An explicitmerge_request_idorissue_idparameter is still honored, as for every flow. Edge case accepted: a checkout goal that happens to be a bare IID or a same-project merge request URL links that merge request. - On server-started runs with a linked merge request, the start service's
BuildAdditionalContextServicereplaces the caller'scode_review_contextwith the one Rails builds, dropping the surface, so the run is a merge request review. This cannot happen for a checkout run, since those are rejected server-side. - Flow configuration selection is unchanged: the CLI sends its own flow config coordinates to the Duo Workflow Service.
- Privileges: no change needed. The create endpoint clamps client-supplied privileges against governance and never applies the definition's list.
- Reporting: a checkout run is identifiable by
workflow_definition = 'code_review/v1'plus a clientenvironment(the CLI sendside), since merge request runs arrive asambientorweb.
Follow-up to agree with the Code Review team (flow side, ai-assist project): the flow should treat an absent surface as merge_request, since Rails does not inject it.
References
- Issue: #631206
- Goal validation origin: !250117 (merged)
Screenshots or screen recordings
Not applicable, backend and API only.
How to set up and validate locally
-
Against GDK, create a workflow:
curl --request POST --header "PRIVATE-TOKEN: $TOKEN" \ --url "http://gdk.test:3000/api/v4/ai/duo_workflows/workflows" \ --data "project_id=<id>&workflow_definition=code_review/v1&environment=ide" \ --data "agent_privileges[]=8&agent_privileges[]=2&pre_approved_agent_privileges[]=8&pre_approved_agent_privileges[]=2" \ --data-urlencode "goal=the working tree" \ --data-urlencode "additional_context[][Category]=code_review_context" \ --data-urlencode 'additional_context[][Content]={"surface":"checkout"}'Expect
201,agent_privileges[8, 2],environmentide; the stored goal isthe working treeandmerge_request_idisnull. -
Same request without the
additional_contextlines. Expect400with the existing message about a merge request IID or URL. -
Same as step 1 plus
start_workflow=true. Expect400:checkout reviews run on the client and cannot be started on the server. No workflow is created. -
Same as step 1 with
goal=. Expect400: the goal must describe what to review. -
Same as step 1 with
{"surface":"branch"}. Expect400:code_review_context.surface must be one of merge_request, checkout. -
Merge request path unchanged: same as step 1 without
additional_contextand withgoal=<open merge request IID>. Expect201and the merge request linked.
MR acceptance checklist
Evaluated against the MR acceptance checklist. No migration, no feature flag.