Add statuses filter argument to duoWorkflowWorkflows query

What does this MR do and why?

Adds a statuses argument to the duoWorkflowWorkflows GraphQL query, so callers can filter Duo Agent Platform sessions by exact status instead of only the coarser statusGroup. The new argument is also threaded through the list_duo_sessions MCP tool.

This matters because the AWAITING_INPUT status group lumps together input_required (session resting between chat turns, nothing needed) with plan_approval_required and tool_call_approval_required (session blocked on someone's approval). There was no way to ask for just "sessions waiting on my decision". statusGroup and statuses are mutually exclusive: passing both returns an error rather than silently combining filters. No UI changes — this is a GraphQL API and MCP tool change only.

This builds on a community contribution that did the original GraphQL work, much appreciated. On top of that, this MR:

  • Fixes a bug where the finder mapped the status enum a second time, raising NoMethodError on any real query.
  • Moves test coverage from a resolver unit spec to the GraphQL request spec.

Database review — statuses filter

New scope Ai::DuoWorkflows::Workflow.in_statuses, used by WorkflowsFinder#resolve_statuses. No migration, no new index, no bulk operation. status IN (7, 8) is plan_approval_required and tool_call_approval_required. LIMIT 20 is the default GraphQL connection page size.

1. Query.duoWorkflowWorkflows(statuses:)

SELECT "duo_workflows_workflows".* FROM "duo_workflows_workflows"
WHERE "duo_workflows_workflows"."user_id" = 11001664
  AND "duo_workflows_workflows"."status" IN (7, 8)
ORDER BY "duo_workflows_workflows"."created_at" DESC
LIMIT 20 OFFSET 0

Plan: https://postgres.ai/console/gitlab/gitlab-production-main/sessions/57763/commands/162383

2. Project.duoWorkflowWorkflows(statuses:)

SELECT "duo_workflows_workflows".* FROM "duo_workflows_workflows"
WHERE "duo_workflows_workflows"."user_id" = 11001664
  AND "duo_workflows_workflows"."project_id" = 278964
  AND "duo_workflows_workflows"."status" IN (7, 8)
ORDER BY "duo_workflows_workflows"."created_at" DESC
LIMIT 20 OFFSET 0

Plan: https://postgres.ai/console/gitlab/gitlab-production-main/sessions/57763/commands/162391

3. Existing statusGroup: AWAITING_INPUT, for comparison

Same index, same plan node, same filter mechanism, so this change introduces no new access pattern. Four of the six existing status groups (paused, completed, failed, canceled) already map to a single status value, so status IN (<one value>) is not new either.

SELECT "duo_workflows_workflows".* FROM "duo_workflows_workflows"
WHERE "duo_workflows_workflows"."user_id" = 11001664
  AND "duo_workflows_workflows"."status" IN (6, 7, 8)
ORDER BY "duo_workflows_workflows"."created_at" DESC
LIMIT 20 OFFSET 0

Plan: https://postgres.ai/console/gitlab/gitlab-production-main/sessions/57763/commands/162400

References

Screenshots or screen recordings

No UI changes — this is a GraphQL API and MCP tool change.

Before After

How to set up and validate locally

  1. Open the GraphQL explorer at http://gdk.test:3000/-/graphql-explorer. Any signed-in user works — the query returns your own sessions. In the GDK the seeded sessions belong to root, so sign in as root to have data to look at.

  2. Compare the coarse filter with the exact one:

    {
      duoWorkflowWorkflows(statusGroup: AWAITING_INPUT, first: 100) {
        nodes {
          statusName
        }
      }
    }
    {
      duoWorkflowWorkflows(
        statuses: [PLAN_APPROVAL_REQUIRED, TOOL_CALL_APPROVAL_REQUIRED]
        first: 100
      ) {
        nodes {
          statusName
        }
      }
    }

    The first returns a mix of input_required, plan_approval_required, and tool_call_approval_required; the second returns only the two approval states. That difference is the point of this MR.

  3. Pass both statusGroup and statuses together and expect the error Only one of [statusGroup, statuses] arguments is allowed at the same time.

first: 100 is needed because the connection has no count field and the default page size is 20.

Validating the MCP tool path

A real MCP client needs a personal access token with the mcp scope. Quicker, in rails console:

svc = Mcp::Tools::DuoWorkflows::ListDuoSessionsService.new(name: 'list_duo_sessions')
svc.set_cred(current_user: User.find_by(username: 'root'))
svc.execute(request: nil, params: { arguments: { statuses: %w[plan_approval_required tool_call_approval_required] } })

It rejects an unknown status name, and rejects status_group and statuses together.

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 Fred de Gier

Merge request reports

Loading
Loading