Add ai_planning_enabled field to work item agent plan widget
What does this MR do and why?
Implements the ai_planning_enabled field on work items, exposed and mutable via GraphQL. This is the backend part of #608882 (closed).
The Work Plan widget currently renders unconditionally on every work item. This MR adds a per-work-item ai_planning_enabled flag so the frontend can conditionally render the widget and its "Plan" CTA.
Changes
Database migration:
- Adds
ai_planning_enabled boolean NOT NULL DEFAULT falsecolumn towork_item_agent_planstable
Model (WorkItems::AgentPlan):
- Adds validation preventing
ai_planning_enabledfrom being set back tofalseoncetrue(opt-in only)
GraphQL field (WorkItemWidgetAgentPlan):
- Adds
aiPlanningEnabled: Boolean!field - Inherits
workplanfeature flag gating from the existing widget
GraphQL mutation (WorkItemEnableAiPlanning):
- Accepts
workItemId(global ID) - Gated behind the
workplanfeature flag - Creates the
AgentPlanif it does not exist yet, then setsai_planning_enabled = true - Returns the updated work item
- Enforces
update_work_itemauthorization
Data sync:
- Carries
ai_planning_enabledover to the target plan when a work item is moved
Tests:
- Model spec: validation that
ai_planning_enabledcannot be unset - GraphQL type spec:
aiPlanningEnabledfield included - Mutation request spec: feature flag gating, authorization, create/update/idempotent paths, concurrent-creation retry, save failure
Naming
The field and mutation were originally called ai_enabled and WorkItemSetAiEnabled. Both were renamed after review, because they read as if AI were being turned on for the entire work item rather than for planning on the agent plan widget, and because "set" suggested a toggle when the flag is one-way. See the discussion.
References
- Work item: #608882 (closed)
- Frontend task: #608883 (closed)
Screenshots or screen recordings
N/A — backend-only change.
How to set up and validate locally
- Enable the feature flag for a group:
Feature.enable(:workplan, Group.find_by_full_path('your-group')) - Create a work item in that group.
- Run the mutation:
mutation { workItemEnableAiPlanning(input: { id: "gid://gitlab/WorkItem/123" }) { workItem { widgets { ... on WorkItemWidgetAgentPlan { aiPlanningEnabled } } } errors } } - Verify
aiPlanningEnabledreturnstrue. - Verify the mutation is a no-op when called again (idempotent).
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.