Add AI governance metrics GraphQL API

What does this MR do and why?

Exposes the AI Governance Dashboard's AI Sessions and AI Agents KPI metrics over GraphQL, so the dashboard frontend can render the summary tiles and sparklines. Adds an aiGovernanceMetrics(timeframe:) field to both Group and Project that resolves through the Ai::Governance::MetricsService from !243933 (merged).

The aggregation services this resolves through merged in !243933 (merged), and this MR now targets master with only the GraphQL layer in the diff. The ai_governance_dashboard feature flag and the dashboard tab that hosts these tiles were introduced in !243109 (merged). The dashboard integration MR !245880 (closed) consumes this exact query shape; its query documents validate against this schema with no errors.

  • aiGovernanceMetrics returns sessions and agents, each an AiGovernanceKpi with count (current window), previousCount (preceding window of equal length, for the delta badge), and trend (zero-filled bucketed series for the sparkline).
  • AiGovernanceMetricsTimeframe offers LAST_24_HOURS (hourly buckets), LAST_7_DAYS, and LAST_30_DAYS (daily buckets); defaults to LAST_7_DAYS.
  • The fields are experiment-marked and return null while ai_governance_dashboard is disabled, so nothing is exposed until the dashboard is rolled out. No changelog entry, per the experiment-behind-a-default-off-flag convention.
  • The resolver checks the service result and returns null if it comes back as an error ServiceResponse. A backend failure surfaces as an absent field rather than an AiGovernanceMetrics object full of nulls.
  • The field carries complexity: 10 on both Group and Project. Each call runs two aggregate queries, so this stops it being fanned out across a projects connection: projects(first: 100) { aiGovernanceMetrics } costs 1000 against the 200 ceiling and is rejected before any query runs.

Authorization

The resolver authorizes :read_agent_artifacts against the parent group or project, mirroring the duoWorkflowSessionArtifacts field on the same dashboard. This is a custom-role ability (requires the custom_roles license); a group-level grant cascades to the group's projects.

GraphQL query

query {
  group(fullPath: "gitlab-org") {
    aiGovernanceMetrics(timeframe: LAST_7_DAYS) {
      sessions { count previousCount trend { bucketStart count } }
      agents { count previousCount trend { bucketStart count } }
    }
  }
}

project(fullPath:) exposes the same field with the same shape. Response:

{
  "data": {
    "group": {
      "aiGovernanceMetrics": {
        "sessions": {
          "count": 42,
          "previousCount": 30,
          "trend": [
            { "bucketStart": "2026-06-30T00:00:00Z", "count": 5 },
            { "bucketStart": "2026-07-01T00:00:00Z", "count": 8 }
          ]
        },
        "agents": {
          "count": 3,
          "previousCount": 2,
          "trend": [
            { "bucketStart": "2026-06-30T00:00:00Z", "count": 2 },
            { "bucketStart": "2026-07-01T00:00:00Z", "count": 3 }
          ]
        }
      }
    }
  }
}

How to validate

In a Rails console, as a user with :read_agent_artifacts on the group, with the flag enabled:

Feature.enable(:ai_governance_dashboard)
user  = User.find_by_username('...')
query = <<~GQL
  query {
    group(fullPath: "gitlab-org") {
      aiGovernanceMetrics(timeframe: LAST_7_DAYS) {
        sessions { count previousCount trend { bucketStart count } }
        agents { count previousCount trend { bucketStart count } }
      }
    }
  }
GQL
GitlabSchema.execute(query, context: { current_user: user }).to_h

On master: group.aiGovernanceMetrics is not a field — the query fails with a Field 'aiGovernanceMetrics' doesn't exist on type 'Group' error.

On this branch: returns the sessions / agents payload above. With the flag disabled (Feature.disable(:ai_governance_dashboard)), aiGovernanceMetrics resolves to null.

Edited by Andrew Jung

Merge request reports

Loading
Loading