Implement work_item_status_changed event trigger

What does this MR do and why?

Adding WorkItem Status Changed event trigger

Addresses: #599983 (closed)

Demo

Validate locally (previous verification with FE changes - won't work anymore since this now only contains BE changes)

See section below for BE verification.

  • Enable the feature flag work_item_status_changed_flow_trigger
  • Add a custom flow. You can use this one
Custom Flow to analyze an issue
version: "v1"
environment: ambient

flow:
  entry_point: "issue_analyzer"

components:
  - name: "issue_analyzer"
    type: AgentComponent
    prompt_id: "issue_analysis_prompt"
    inputs:
      - from: "context:goal"
        as: "issue_iid"
      - from: "context:project_id"
        as: "project_id"
    toolset:
      - "get_issue"
      - "list_issue_discussions"
      - "list_issues"
      - "get_repository_file"
      - "list_repository_tree"
      - "create_issue_note"
    ui_log_events:
      - "on_agent_final_answer"
      - "on_tool_execution_success"
      - "on_tool_execution_failed"

routers:
  - from: "issue_analyzer"
    to: "end"

prompts:
  - name: "Issue Analyzer"
    prompt_id: "issue_analysis_prompt"
    unit_primitives: []
    prompt_template:
      system: |
        You are an expert software analyst. Your job is to analyze a GitLab issue (work item)
        and post a structured comment with a clear understanding of the problem and an
        actionable implementation plan.

        Steps:
        1. Use get_issue with project_id={{project_id}} and issue_iid={{issue_iid}}
        2. Use list_issue_discussions to retrieve existing comments and context
        3. Use list_repository_tree to understand the project structure if relevant
        4. Use get_repository_file on files mentioned or likely involved in the issue
        5. Post a comment using create_issue_note with the analysis below

        The comment MUST follow this structure:

        ## 🔍 Issue Analysis

        ### Summary
        A concise restatement of the issue in technical terms.

        ### Affected Areas
        List files, modules, or components likely involved based on the description and repo structure.

        ### Root Cause / Problem Statement
        Explain what is broken, missing, or unclear and why.

        ### Implementation Plan
        Numbered steps to address the issue:
        - What needs to be changed or added
        - Which files to modify and how
        - Any dependencies or preconditions
        - Suggested tests or validation steps

        ### Open Questions
        Any ambiguities in the issue that need clarification before or during implementation.

      user: |
        Project ID: {{project_id}}
        Issue IID: {{issue_iid}}
  • Enable it on a project
  • Create an issue on the project
  • Change the status on this issue to anything, and see the flow running and writing a note on the issue.

Verify BE changes using Rails console

# set up

project = Project.find(1000000)
user = User.find(1)

# Create a service account
  service_account = User.create!(
    user_type: :service_account,
    name: 'test-status-trigger-bot',
    username: "test-status-bot-#{SecureRandom.hex(4)}",
    email: "test-status-bot-#{SecureRandom.hex(4)}@test.local",
    confirmed_at: Time.current,
    projects_limit: 0,
    namespace: Namespace.find_by(type: nil) || project.namespace
  )
  project.add_developer(service_account)

# Create a trigger with status_changed filter
  trigger = Ai::FlowTrigger.create!(
    project: project,
    user: service_account,
    event_types: [Ai::FlowTrigger::EVENT_TYPES[:work_item]],
    description: 'Test status changed',
    config_path: '.gitlab/duo/work_item_trigger_test.yml',
    filter: { 'work_item' => { 'rules' => [{ 'field' => 'action', 'operator' => 'in', 'value' => ['status_changed'] }] } }
  )
  puts "Trigger created: #{trigger.id}"

# Verify trigger registration
  puts "Registered: #{Ai::FlowTrigger.registered_for?(project.id, :work_item)}"

# Find a work item
  work_item = project.work_items.last

# Publish a status_changed event — should fire
  Sidekiq.strict_args!(false)
  status = ::WorkItems::Statuses::SystemDefined::Status.find(2)
  cloud_event = WorkItems::StatusChangedEvent.build(
    work_item: work_item, current_user: user, status: status
  )
  Gitlab::EventStore.publish(cloud_event)

# The ExecuteWorkItemStatusChangedWorkflowTriggersWorker as below should be returned verifying the worker was called

=> [#<Gitlab::EventStore::Subscription:0x000000014696eb68
  @condition=
   #<Proc:0x0000000144d67cf8 /Users/sholaq/gitlab-development-kit/gitlab/ee/lib/gitlab/event_store/subscriptions/ai_subscriptions.rb:52 (lambda)>,
  @delay=nil,
  @group_size=10,
  @worker=
   Ai::Catalog::Flows::ExecuteWorkItemStatusChangedWorkflowTriggersWorker>]

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist.

Edited by Shola Quadri

Merge request reports

Loading