Expose user field on SessionArtifactType GraphQL type

What does this MR do and why?

Exposes a nullable user field on Types::Ai::DuoWorkflows::SessionArtifactType so the Agent Artifacts table can display which user initiated a session.

The Ai::DuoWorkflows::SessionArtifact model already has a belongs_to :user association with user_id required and populated from the workflow's user_id on sync, but this user was not exposed via GraphQL.

Changes:

  • SessionArtifactType: adds a nullable user field of type UserType. The resolver handles both object shapes:
    • ActiveRecord path (PostgreSQL finder): reads object.user, preloaded to avoid N+1.
    • Hash path (ClickHouse finder): batch-loads User by user_id via BatchModelLoader.
  • SessionArtifact model: adds scope :with_user using preload(:user) (consistent with the existing with_project scope, and safe across the in-operator optimization subquery rewrite).
  • PostgresqlFinder: chains .with_user after .with_project to preload users for the AR path.
  • ClickHouseFinder: adds user_id to the projected COLUMNS so it is available in the Hash objects returned to the type.
  • GraphQL reference docs: manually updated doc/api/graphql/reference/_index.md with the new user field entry (the gitlab:graphql:compile_docs Rake task requires a database connection not available in this environment; CI will regenerate the full schema).
  • Specs: type spec covers both AR and Hash paths; request spec adds a user field assertion and an N+1 guard.

References

Related to #606639

Screenshots or screen recordings

Screenshot_2026-07-27_at_1.50.22_PM

How to set up and validate locally

  1. In a Rails console, find or create a Ai::DuoWorkflows::SessionArtifact record.
  2. Query the GraphQL API:
    {
      group(fullPath: "<group>") {
        duoWorkflowSessionArtifacts(first: 5) {
          nodes {
            id
            user { username }
          }
        }
      }
    }
  3. Verify the user field returns the correct username.

Database review

New query introduced by scope :with_user, -> { preload(:user) } on Ai::DuoWorkflows::SessionArtifact, used to eager-load the new user GraphQL field on the PostgreSQL path. This uses preload (not includes/joins), so it always runs as a separate query rather than being merged into the session artifacts CTE — a plain primary-key lookup independent of the artifacts query's complexity.

Raw SQL:

SELECT "users".* FROM "users" WHERE "users"."id" IN (1614863, <second_user_id>)

Query plan: https://console.postgres.ai/gitlab/projects/gitlab-production-main/sessions/54079/commands/156478

An N+1 guard test (ee/spec/requests/api/graphql/ai/duo_workflows/session_artifacts_spec.rb) confirms this query runs once per page regardless of how many distinct users are referenced.

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 Scott Hampton

Merge request reports

Loading
Loading