Add reason modal for Duo Slack thumbs-down feedback

What does this MR do and why?

Iteration 2 of Duo agent feedback in Slack: when a user marks a Duo response as Not helpful, a Block Kit modal opens to collect a structured reason (Incorrect or misleading / Didn't do what I asked / Incomplete / Other) plus an optional free-text comment, with a data-sharing disclaimer.

Iteration 1 (!244668 (merged)) shipped the thumbs up/down buttons and click tracking. This MR adds the thumbs-down reason modal on top.

Design decisions

  • Best-effort modal, vote first: the thumbs_down event is tracked on click before views.open is called. A dismissed modal, expired trigger_id, or Slack API failure only loses the optional reason — never the vote.
  • Append-only, last-write-wins: the modal submission appends a second ai_duo_messaging_feedback_submitted event enriched with reason/comment (into extras). Consumers use the latest event per user and workflow, as documented in the event definition.
  • callback_id routing for view_submission: previously all view submissions went straight to the incident modal service. A new CE ViewSubmissionService routes by callback_id (unknown IDs are ignored), and EE injects the Duo feedback handler via prepend_mod — mirroring the existing BlockActionService pattern.
  • Validation: reason: other requires a comment, enforced natively via response_action: errors. Comment length capped at 1000 chars both client-side (max_length) and server-side (truncate).
  • Confirmation: on submit the modal swaps to a "Feedback submitted." view via response_action: update, propagated through the interactions endpoint which previously discarded service payloads.
Demo

How to set up and validate locally

Requires the GDK Slack app setup (see !248037 (merged)). No new OAuth scopes or Slack app manifest changes needed.

  1. Mention the Duo bot in Slack and let a flow complete.

  2. Click Not helpful on the reply — the "Share feedback" modal opens.

  3. Pick Other and submit without a comment — inline validation error appears.

  4. Add a comment and submit — modal swaps to the confirmation view.

  5. Verify both events landed (click + enriched submit):

    UsageEvents::DumpWriteBufferCronWorker.new.perform
    Ai::UsageEvent.where(event: 'ai_duo_messaging_feedback_submitted').last(2).map(&:extras)

    Expect one {feedback: thumbs_down, workflow_id:, surface: slack} and one with reason/comment added.

References

Edited by Jannik Lehmann

Merge request reports

Loading
Loading