Allow adding custom response to Duo question

What does this MR do and why?

Allows for adding custom response to question asked by Duo, rejecting the presented options, this is a follow-up to the feature added in !249919 (merged).

Additionally, we also support caching the response in localStorage (just like regular discussion comments) if user accidentally closes the browser tab, or reloads the page, to allow for recovering the response from cache on page load. The approach uses same mechanism as draft comments.

References

Screenshots or screen recordings

Default State Disabled State Custom response typing Custom Response submitted
image image image image

How to set up and validate locally

Setting up mock data

The backend that posts these questions is still not merged, so post the question yourself. Any comment carrying a json:duo-question fenced block is treated as a Duo question, so nothing needs seeding from the Rails console.

  1. Enable the feature flag in the Rails console: Feature.enable(:duo_workplan_async_flow)
  2. Open any work item you can comment on.
  3. Post a comment with the contents below, using your own user account.
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."}]}
```

The comment starts out as an individual note, and GitLab promotes it to a resolvable thread on the first reply, since Notes::BuildService calls convert_to_discussion!. That is what lets answering resolve the thread, so no DiscussionNote seeding is needed.

Answering resolves the thread, so each question can only be answered once. Post the comment again whenever you want a fresh question to try another path.

Testing the UI

  1. Reload the work item. The question should render with its two options and a text input below them, so answering in your own words is as reachable as picking.
  2. Leave the input empty, or type only spaces. The send button should stay disabled, because whitespace is not an answer.
  3. Type an answer that rejects both options, for example Neither: shed load only above a global threshold., then click the send button. The reply should post inside the same thread, the thread should resolve, and the card should show what you wrote instead of the option list.
  4. Post the question again, then submit an answer with Enter rather than the button. It should behave the same way.
  5. Reload the page. The card should still show your typed answer, which is read back out of the reply rather than from component state.
  6. Check the draft cache. Post a fresh question, type a partial answer, and reload without submitting. The text should come back in the input. It is stored under autosave/<discussion GID>-duo-answer, deliberately separate from the -comment key the thread's reply box uses, so a half-written answer and a half-written reply cannot overwrite each other.
  7. Submit a full answer and confirm the draft is discarded, so the input does not repopulate on the next reload.
  8. Tab to the input and confirm a focus ring appears around the row. GlFormInput draws its border and its focus ring as box-shadows, so the ring sits on the row rather than on the borderless input.
  9. Check both light and dark mode.
  10. Optionally, run Feature.disable(:duo_workplan_async_flow) and confirm the card no longer renders.

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.

Related to #608853 (closed)

Edited by Kushal Pandya

Merge request reports

Loading
Loading