Improve Slack UX: setStatus on request received, skip on_flow_started ack, persist plan block
What does this MR do and why?
Improves the Duo Slack response UX, building on the live progress streaming work in !242201 (merged):
1. on_request_received: Rotating status instead of a static message
Before: Posted a static markdown message ("On it! Working on your request.") into the thread immediately.
After: Calls assistant.threads.setStatus with rotating loading strings. This gives immediate animated feedback without cluttering the thread with a static message. The status clears automatically when the first real message is posted.
The loading strings describe the whole run rather than just agent startup ("is working on your request…", "is thinking it through…"), because when the agent produces no todo list and no monologue this status is the only progress signal the user sees before the final answer. They are built in a method (not a frozen constant) so translations resolve dynamically.
set_status is restored in lib/slack/api.rb (it was removed in !242201 (merged)).
2. on_flow_started: Skip the ack message — keep the status spinning
Before: Posted (or fell back to posting) an acknowledgement message with the session footer link.
After: Only persists session_url in the callback context. No message is posted. The rotating status indicator keeps spinning until on_progress posts the first real content (plan block or agent monologue). This keeps the experience in Slack for short tasks rather than pushing the user to GitLab.
As a fallback, if on_progress never fires (no todo list and no monologue), deliver_result posts the final answer directly. The session URL is persisted unconditionally so the footer is always available.
3. on_progress: Post the first real content, persist status_ts
After:
- When no
status_tsexists yet,on_progressposts a new message with the first real content (the status indicator clears automatically when a message is posted in the thread) and records itsts. ProgressDeliveryWorkernow persistsstatus_tsalongside the progress cursor, so the next progress tick edits the existing message instead of posting a duplicate.
4. deliver_result: Rebuild the plan block from the terminal checkpoint
Before: deliver_result replaced the entire message with just the final answer, wiping the plan block.
After:
deliver_resultnow receives theworkflowand reads the final todo list fromAi::DuoWorkflows::Workflow#latest_ui_chat_log(the cumulativeui_chat_logof the latest checkpoint).- It prepends a freshly built plan block (if the run had a todo list) before the final answer, so users can inspect the task list after the run.
Building the plan from the terminal checkpoint (rather than freezing the last on_progress render in the callback context) keeps completed tasks from being shown as in_progress in the final message: the final todo_write (all complete) lands in the terminal checkpoint after live progress stops, so reading it here keeps the plan in sync with the final answer. This also removes the stale-state persistence path (last_plan_block).
Workflow#latest_ui_chat_log is added so the messaging workers and adapters read the final conversation state without reaching into the checkpoints association themselves; CallbackWorker#extract_final_message now takes the chat log directly.
References
- Issue: #606302 (closed)
- Base MR (live progress streaming): !242201 (merged)
- Release requirements: gitlab-org#22438 (closed)
- Slack
assistant.threads.setStatusdocs: https://docs.slack.dev/reference/methods/assistant.threads.setStatus
Screenshots or screen recordings
| Before | After |
|---|---|
| Static "On it! Working on your request." message posted immediately | Rotating status indicator ("is working on your request…" etc.) shown immediately |
| Plan block wiped when final answer is delivered | Plan block (rebuilt from the terminal checkpoint) preserved above final answer |
How to set up and validate locally
- Configure a Slack integration with a Duo Developer workflow.
- Trigger a workflow by
@mention-ing the app with something like "can you simulate a small task with 3 todos? Think out loud in between when you work through your tasks." - Watch the thread:
- You should see the rotating status indicator immediately (no static message).
- When the agent starts working, the first real content (plan or monologue) appears as a message.
- When the workflow finishes, the final answer appears with the plan block above it (if the agent used a todo list), with all tasks shown in their final state.
- For a short task with no todo list and no monologue, the status spins until the final answer is posted directly.
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.