Add WorkItemGenerateWorkplan mutation for async workplan flow
What does this MR do and why?
Adds an async, non-interactive way to generate a work item's workplan: the WorkItemGenerateWorkplan GraphQL mutation and its backing Ai::DuoWorkflows::GenerateWorkplanService, which start a Duo Agent Platform flow (workplan/v1) instead of requiring a live Duo Chat session.
This is the first backend building block for the async work item refinement loop described in #605157 (closed): today a workplan can only be drafted interactively via Duo Chat, which ties one user to one plan at a time. Moving generation into a CI-backed flow lets a user kick off several refinements in parallel and get notified only when input is needed, instead of babysitting one chat session per work item.
Both the mutation and service are self-contained behind the duo_workplan_async_flow feature flag (default off, introduced in !248543 (merged)).
How it works
workItemGenerateWorkplan(id: WorkItemID!)authorizes:update_work_itemon the work item, then delegates toGenerateWorkplanService, returning the startedworkflow(orerrors).- The service validates, in order:
- the work item belongs to a project (the flow runs in CI, so group-level work items aren't supported) →
PROJECT_REQUIRED_ERROR duo_workplan_async_flowis enabled for the project →FEATURE_DISABLED_ERROR- the
workplan/v1foundational flow definition exists (Ai::Catalog::FoundationalFlow) →FLOW_UNAVAILABLE_ERROR - no non-terminal workflow is already running for this work item + flow definition →
WORKFLOW_ALREADY_RUNNING_ERROR
- the work item belongs to a project (the flow runs in CI, so group-level work items aren't supported) →
- The goal is built by
Ai::Catalog::GoalTemplates::Workplan, a one-line template that only names the target work item URL. Plan format, ambiguity handling, and the question-escalation protocol intentionally live solely in theworkplan/v1system prompt ingitlab-ai-gateway, not here — a single source of truth for agent behavior instead of a second copy to keep in sync. - Starts the flow via the existing
Ai::DuoWorkflows::CreateAndStartWorkflowService. WORKFLOW_DEFINITION_REFERENCE = 'workplan/v1'is the string the widget status field, the discussion-resolved worker, and the frontend "Generate workplan" button will all key off of in follow-up MRs — none of those are part of this change.
Also in this MR
- A follow-up commit fixes 3 spec failures surfaced by isolating this change from the full branch:
- Added the
with_non_terminal_statusscope toAi::DuoWorkflows::Workflow(present in the full branch, missing here since this is a split-out substrate MR). - Updated the foundational-flow count assertion in
seed_foundational_flows_service_spec.rb(11 → 12) for the newworkplan/v1entry. - Raised the query-limit assertion in
groups/update_service_spec.rb(140 → 150); the new foundational flow pushedCascadeSyncFoundationalFlowsWorker's query count to 144 (tracked in #598945).
- Added the
Out of scope (this iteration)
Per the parent work item's constraints — tracked as follow-ups, not this MR:
- Surfacing workflow progress on the work item (widget status field).
- Reacting to flow completion (discussion-resolved worker).
- The frontend entry point (button).
- Work items with children (no hierarchies) and live collaborative chat (comments only) remain unsupported.
References
- #605157 (closed) (work item refinement loop this unblocks)
- Parent epic: gitlab-org#22445
- Split from: !247170 (closed)
- Query-limit follow-up: #598945