Keep Duo mention started note as durable record

What does this MR do and why?

Implements the UX improvement requested in #602991: when a Duo mention workflow completes, we remove the transient "started" (thinking) system note and append a "View session" link to the result message, so the session stays referenced close to the outcome instead of leaving a leftover system note in the thread.

Before: The "started … session N and will let you know when it's finished" note stayed in the thread after completion (or behaved inconsistently across surfaces), and on the bot path it could disappear/reorder unexpectedly on live update.

After: While the flow runs, the started note shows an animated loader. Once the result lands, the started (thinking) note is destroyed and the reply note is posted with a [View session](url) link appended at the bottom. This works for both the @GitLabDuo bot path and the Duo Developer service-account path on MRs.

This direction was agreed in the review threads (see discussion): show the thinking message, remove it once the result lands, and append the session details to the result message (starting small with a simple "View session" link, taking design inspiration from the Code Review path as a follow-up).

Changes

Backend

  • duo_mention_started builds SystemNoteMetadata (action: duo_mention_started) and Notes::NoteDuoMetadata (workflow_id, namespace_id) on the created note, so the workflow link is queryable without parsing the note body.
  • deliver_result/deliver_error load the started note once (load_started_note), reuse it for both the workflow lookup and destruction (no duplicate query), then destroy it and post the reply. destroy_started_note rescues ActiveRecord::RecordNotDestroyed and tracks the exception so a destroy failure never blocks the reply.
  • deliver_result appends a [View session](url) link to the reply via append_session_link. A blank result message still posts the session link rather than being swallowed.
  • Session wording drops the word "mention" → "session N".
  • NoteEntity exposes duo_session_status (the workflow's status_name, not the raw enum), gated on the duo_mention_started action, used by the frontend to route rendering and drive the spinner.

Frontend

  • Routing of the styled Duo system note is centralised in a single shouldRenderAsDuoSystemNote(note) helper (~/notes/utils.js), used by both discussion_notes.vue and notes_app.vue. It routes on the presence of duo_session_status (covers both the bot and service-account mention notes) and keeps the duo_code_review_bot author check for the separate Duo Code Review progress note (which has no duo_session_status).
  • Removal of the started (thinking) note on reply is centralised in a single findStartedNoteForReply(notes, discussions) helper (~/notes/utils.js), called from both stores/actions.js and legacy_notes/actions.js (kept symmetric, in fetchUpdatedNotes). The helper only removes a started note when an incoming reply note shares a discussion that actually contains a started note (duo_session_status present), so ordinary comments no longer trigger removal.
  • duo_code_review_system_note.vue: the loader is shown and animated only while the session is in progress (created/running) and hidden once terminal (finished/failed/stopped) or waiting; absent status falls back to animated for notes created before this field existed.

Out of scope / follow-ups

  • Issues (work items) use a separate GraphQL notes stack and do not render the styled card/spinner. The backend durability (persist + destroy-on-completion) works there, but the visual unification is a follow-up.
  • A richer styled "View session" treatment (matching the Code Review design) instead of the simple markdown link.
  • The shared DuoCodeReviewSystemNote avatar styling differs slightly from a normal note avatar (pre-existing).
  • MR-vs-issue system-note unification and any public API exposure.

References

How to set up and validate locally

Prereqs: a DAP-onboarded project with the mention foundational flow(s) enabled and a :mention service-account trigger, and the Duo Workflow executor running in GDK.

# Duo Developer @mention path
Feature.enable(:ai_use_messaging_adapter_for_mentions)
  1. Duo Developer @mention (MR thread): comment @<duo-developer-service-account> … on an MR.
    • A started note appears — "started session N and will let you know when it's finished" — with an animated loader.
    • When the flow completes, the started (thinking) note is removed and the reply note is posted with a [View session](url) link appended at the bottom — without reloading.
  2. @GitLabDuo MR mention (requires duo_foundational_flows_enabled + the code-review catalog item enabled on the project): comment @GitLabDuo … on an MR.
    • Same started → removed-on-completion behavior, and the started note no longer disappears or jumps position when unrelated comments arrive on live update.
  3. Failure path: trigger a flow error (e.g. by cancelling the session) → the started note is removed and an error reply is posted.
  4. Try the same on an issue (the UI will look a bit different there but this is parked for a follow-up).

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 Thomas Schmidt

Merge request reports

Loading