Enable AI planning when an agent plan is written

What does this MR do and why?

The Workplan widget stays hidden until work_item_agent_plans.ai_planning_enabled is true. Until now only the WorkItemEnableAiPlanning mutation (called by the Plan CTA) and the move data sync ever set that field to true. Any other write path left it false, so a plan written through workItemUpdate with agentPlanWidget, the REST equivalent, Duo's update_work_item tool, or the MCP create_work_item tool produced a plan the user could never see. The UI showed the Plan CTA even though a plan already existed.

The same problem hits the readiness score, since the score renders inside the same widget. The team is building toward showing a score before a plan exists and a better score once it lands, so the score path needs the widget visible too.

The fix is in ee/app/services/work_items/callbacks/agent_plan.rb. The callback now sets ai_planning_enabled = true whenever a write leaves the agent plan with content or a readiness score, regardless of which caller wrote it. All write paths go through this callback, so GraphQL, REST, Duo tools, MCP tools, and the UI are covered by one change. There is no new GraphQL field or argument, and no new MCP parameter; callers cannot pass the flag directly.

This is enabling only. Clearing plan content does not turn the flag back off; that is already enforced by the model's ai_planning_enabled_cannot_be_unset validation. No feature flag check was added to the callback itself, because the agent_plan widget is already dropped from a work item type's widget list when workplan is off (ee/app/models/ee/work_items/types_framework/system_defined/type.rb), so the callback only runs when workplan is on. The score path stays behind workplan_score as before.

Out of scope: rows written before this change still have ai_planning_enabled set to false. A one-off post migration (20260821143557_update_all_work_item_agent_plans_ai_planning_enabled_to_true) ran on GitLab.com around 2026-08-27 to backfill existing rows, but anything written through the API between that migration and this fix landing is still hidden. The table has no content column (plan content lives in object storage), so a scoped backfill is not possible; any further backfill has to touch every row. Tracked in #619376 (closed).

How to set up and validate locally

  1. Enable the workplan feature flag for your root namespace.
  2. Create a work item and do not click Plan.
  3. Write a plan through GraphQL: workItemUpdate with agentPlanWidget: { content: "# Plan" }.
  4. Query the work item's WorkItemWidgetAgentPlan.aiPlanningEnabled. It should be true.
  5. Reload the work item in the UI. The Workplan widget shows with the content and the Plan CTA is gone. Before this change the CTA still showed and the plan did not render.

Tests added in ee/spec/services/work_items/callbacks/agent_plan_spec.rb cover:

  • content write on create and on update enables the flag
  • score-only create enables it
  • score-only update on a plan with no content yet enables it
  • clearing the content does not enable it
  • clearing the content on an already-enabled plan leaves it enabled
  • with workplan_score off, a score-only param changes neither the score nor the flag

References

  • #626741 (closed) (Automatically enable AI planning when a workplan payload exists on a work item)
  • #619376 (closed) (backfill follow-up for pre-fix rows)
  • !249644 (merged) (added the ai_planning_enabled field)
  • Reported in the #s_plan Slack channel on 2026-08-31.

Merge request reports

Loading
Loading