Render Duo clarifying questions as interactive cards
What does this MR do and why?
Renders the clarifying questions the Duo workplan flow posts on a work item as an interactive card instead of plain prose. The agent posts each question as a resolvable discussion carrying a machine-readable payload in a fenced json:duo-question block. We read that payload, draw the question with its options, and on click post the chosen label as a reply that resolves the thread.
The answered state survives a reload, since the chosen option is recovered from the reply, which carries the option id in an HTML comment. While wiring that up we found work_item_discussion.vue was already passing :discussion-id to work_item_note.vue without the prop being declared, so it silently arrived as a fallthrough attribute. That is now declared, otherwise answering would have posted a new top-level comment instead of a threaded reply, and would never have resolved the thread.
The raw JSON is hidden in CSS rather than by the component, so it never flashes before hydration and stays hidden if the bundle never runs. FOSS renders nothing, through a CE no-op counterpart imported with ee_else_ce.
Gated on the existing duo_workplan_async_flow flag, which is disabled by default and already pushed to the frontend. No new flag and no new flag exposure. No changelog entry, since the change sits behind a disabled flag.
References
Screenshots or screen recordings
Important
This MR implements the foundation for rendered question so it supports single-select only for now. Free-text (#608853 (closed)) and Multi-select (#608852 (closed)) responses are tracked in their own issues.
| Light | Dark | |
|---|---|---|
| Question awaiting response | ||
| Answer selected |
How to set up and validate locally
Setting up mock data
The backend that posts these questions is not merged yet, so we need to manually seed a question either via Rails console or by posting a comment ourselves that includes json:duo-question fenced block present.
- Enable the feature flag in the Rails console:
Feature.enable(:duo_workplan_async_flow) - Pick any work item you can comment on, and note its project path and IID.
- Open Rails console (
bin/rails console) and seed a question with the snippet below, editing only the first two lines.type: 'DiscussionNote'is what makes it a resolvable discussion, which is what the flow itself creates:
project = Project.find_by_full_path('gitlab-org/gitlab-shell') # your project
work_item = project.work_items.find_by_iid(48) # your work item
body = <<~MD
When a client exceeds the rate limit, should the request be rejected immediately or throttled?
```json:duo-question
{"type": "closed", "question": "Reject or throttle over-limit requests?", "options": [{"id": "reject", "label": "Reject immediately", "description": "Return an error and close the channel.", "recommended": true}, {"id": "throttle", "label": "Throttle", "description": "Queue the request and run it when the rate allows."}]}
```
MD
Notes::CreateService.new(
project, User.find_by_username('root'),
noteable: work_item, note: body, type: 'DiscussionNote'
).executeAlternatively, you can manually post a comment using your user account with following contents and the parser will treat it as Duo question.
When a client exceeds the rate limit, should the connection be rejected immediately (the new command/session is refused with an error message), or should it be throttled/delayed (the command is queued and executed after a brief wait)?
```json:duo-question
{"type": "closed", "question": "When a client exceeds the rate limit, should the excess request be rejected immediately or throttled/delayed?", "options": [{"id": "reject", "label": "Reject immediately", "description": "Return an error to the client right away (e.g. 'rate limit exceeded') and close the channel, similar to how too-many-concurrent-sessions is handled today.", "recommended": true}, {"id": "throttle", "label": "Throttle / delay", "description": "Hold the request in a queue and execute it once the rate allows, adding latency but not failing the client outright."}]}
```Testing the UI
- Reload the work item. The question should render as a card with two options, a
Recommendedbadge on the first, and no raw JSON visible. - Click an option. The reply should post inside the same thread, and the thread should resolve.
- Reload the page. The card should still show the chosen option rather than reverting to the option list.
- Check both light and dark mode. Options sit flat on the comment surface in light, and raised above it in dark.
- Optionally, run
Feature.disable(:duo_workplan_async_flow)and confirm the card no longer renders.
MR acceptance checklist
This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.
- I have evaluated the MR acceptance checklist for this MR.