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_downevent is tracked on click beforeviews.openis called. A dismissed modal, expiredtrigger_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_submittedevent enriched withreason/comment(intoextras). Consumers use the latest event per user and workflow, as documented in the event definition. callback_idrouting forview_submission: previously all view submissions went straight to the incident modal service. A new CEViewSubmissionServiceroutes bycallback_id(unknown IDs are ignored), and EE injects the Duo feedback handler viaprepend_mod— mirroring the existingBlockActionServicepattern.- Validation:
reason: otherrequires a comment, enforced natively viaresponse_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.
-
Mention the Duo bot in Slack and let a flow complete.
-
Click Not helpful on the reply — the "Share feedback" modal opens.
-
Pick Other and submit without a comment — inline validation error appears.
-
Add a comment and submit — modal swaps to the confirmation view.
-
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 withreason/commentadded.
References
- Resolves #603660 (closed)
- Iteration 1: !244668 (merged)