Hand server-side Duo turns off to Workhorse detached

What does this MR do and why?

A server-side Duo Agent Platform turn (Slack, note replies) runs in ServerSideTurnWorker. The job holds a Sidekiq thread for the whole turn, only to read the stream from POST /api/v4/ai/duo_workflows/workflows/:id/execute. !264218 (merged) lets Workhorse run the turn detached: it answers 202 once the run has started and keeps it going on its own. This MR is the Rails side, behind the new wip flag duo_workflow_detached_server_side_execution (actor: the workflow's user).

  • Pre-authorization. execute takes a new detached parameter. It returns Detached: true only when the caller sent detached=true and the flag is on for the user. Workhorse reads only that field, so the flag decides, not the caller.
  • Worker. With the flag on, the worker sends detached=true. On a 202 it returns without delivering. If pre-authorization didn't grant the detached run, Workhorse streams the turn and the worker runs the existing blocking path, so a flag change between the two checks is harmless. Errors before the 202 are reported as today.
  • Delivery. Finished, failed and stopped turns are already delivered by CallbackWorker from their events. A turn that pauses for the user (input_required) was only delivered inline by the worker: CallbackWorker calls on_input_required, which no adapter implements. It now delivers the answer for detached turns. The worker sets a detached_turn marker in the callback context before the request (the turn can pause before Workhorse answers), and per_turn_reset clears it. It is also a session-state key, so a restarted or child session doesn't inherit it. CallbackWorker delivers only while the workflow is still input_required, so a late event can't post this turn's answer over the next one.

Why keep the job at all? Some callers of ExecuteRunService run in web requests: the Slack approval buttons (Slack expects an answer within 3 seconds), the MCP agent-session start and tool-approval resolution. Calling Workhorse from there would make Puma call itself through Workhorse and wait up to 10 seconds, with pre-authorization needing a second Puma thread meanwhile. The job now finishes in seconds instead.

With the flag off, nothing changes. The marker is never set, so CallbackWorker doesn't deliver on input_required.

Known limitations, tracked on the work item
  • A 409 or 503 before the 202 is reported to the user as a failure, as in the blocking mode. A Sidekiq job re-pushed while its first run is going would also see a 409; retrying or ignoring it is a follow-up.
  • A detached run that ends while the workflow is still running (deploy, stream error, the 55-minute limit) is caught by the stuck-workflow cleanup after about 30 minutes, which delivers a failure. The segment-end callback is the follow-up that fixes this.
  • The flag must stay off until the Workhorse MR is deployed and the Duo Workflow Service lists WORKHORSE_RUN_LIMIT as an infrastructure stop reason. All Sidekiq nodes must also run this code, since an older CallbackWorker doesn't deliver detached answers.

References

Screenshots or screen recordings

Not applicable: no UI change.

How to set up and validate locally

Needs Workhorse built from !264218 (merged).

  1. In the Rails console, enable the flag for your user:
    Feature.enable(:duo_workflow_detached_server_side_execution, User.find_by_username('root'))
  2. Start a server-side turn, for example by mentioning @GitLabDuo in a note on a project with Duo Agent Platform enabled, or by messaging Duo in Slack.
  3. In the Workhorse log, look for duo workflow: detached run accepted. The Ai::Messaging::ServerSideTurnWorker job in the Sidekiq log finishes within seconds.
  4. The answer is still posted, now by Ai::Messaging::CallbackWorker. Reply to it to check that the next turn is answered too.
  5. Disable the flag and repeat: the worker job now lasts for the whole turn, as before.

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.

Edited by Igor Drozdov

Merge request reports

Loading
Loading