Create an open decision when Duo posts a clarifying question

What does this MR do and why?

Creates an open (unresolved) Decision Log entry whenever the Duo workplan flow posts a clarifying question on a work item. The decision holds the question (truncated to 255 characters), the options Duo offered with their descriptions and which one it recommended (up to 5, the most a decision holds), the question's thread (discussion_id), and the workflow run that asked. Its context is a fixed line: "GitLab Duo raised this question during review of the work item."

This is the flow the Decision Log was designed for: a decision opened when the question is asked and resolved once it's answered. With the decision already there, the frontend only has to resolve it when the thread is resolved, which is #608859 (closed).

The hook lives in the EE Notes::CreateService#when_saved, next to where a note posted by a Duo workflow is already linked to that workflow. It only acts on workplan/v1 runs, and only when the note's author is the run's own service account, since the workflow ID comes from a client-supplied header. It's best effort, so a failure to record the decision never fails the comment. It only runs where the Decision Log is available on the work item, which covers the decision_log feature flag, the ai_workflows licence, and the work item type.

The question payload parsing moves out of Gitlab::WorkItems::DuoQuestionEmailBody into a shared Gitlab::WorkItems::DuoQuestion, which the email rewrite and the new hook both use. It now also reads the question field. Email output is unchanged.

Everything is behind the decision_log flag, which is off by default, so no changelog entry.

References

Screenshots or screen recordings

Triggering Plan Awaiting Input (Decision Created)

How to set up and validate locally

  1. In a Rails console: Feature.enable(:duo_workplan_async_flow), Feature.enable(:workplan) and Feature.enable(:decision_log).
  2. Pick a work item whose description leaves something open that the workplan flow has to ask about, and click Plan in the work item header.
  3. When the run pauses on a question, open the Decision log from the work item header. It should list an open decision per question, titled with the question.
  4. A comment that doesn't carry a question (for example the flow's "workplan is ready for review" comment) should create no decision, and neither should a person posting the same payload themselves.

Locally, the flow's research stage sometimes pauses with input_required without posting its question. When that happens, post a question as the run's service account from a Rails console with the snippet below, which goes through the same server path the flow does. Swap the project path and iid for your own work item.

work_item = WorkItem.find_by(project: Project.find_by_full_path('gitlab-org/gitlab-shell'), iid: 48)
workflow = work_item.duo_workflows.with_workflow_definition('workplan/v1').order(:id).last
payload = { type: 'closed', question: 'Who should be exempt from the rate limit?', options: [
  { id: 'no_one', label: 'No one', recommended: true },
  { id: 'deploy_keys', label: 'Deploy keys only' }
] }
body = "Who should be exempt?\n\n```json:duo-question\n#{Gitlab::Json.dump(payload)}\n```"

Gitlab::SafeRequestStore.ensure_request_store do
  Gitlab::Auth::Identity.link_from_scoped_user(workflow.service_account, workflow.user)

  Gitlab::ApplicationContext.with_context(duo_workflow_id: workflow.id.to_s) do
    Notes::CreateService.new(work_item.project, workflow.service_account,
      note: body, noteable_type: 'Issue', noteable_id: work_item.id).execute
  end
end

work_item.decisions.last

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.

Edited by Kushal Pandya

Merge request reports

Loading
Loading