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.
aiGovernanceMetricsreturnssessionsandagents, each anAiGovernanceKpiwithcount(current window),previousCount(preceding window of equal length, for the delta badge), andtrend(zero-filled bucketed series for the sparkline).AiGovernanceMetricsTimeframeoffersLAST_24_HOURS(hourly buckets),LAST_7_DAYS, andLAST_30_DAYS(daily buckets); defaults toLAST_7_DAYS.- The fields are
experiment-marked and returnnullwhileai_governance_dashboardis 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
nullif it comes back as an errorServiceResponse. A backend failure surfaces as an absent field rather than anAiGovernanceMetricsobject full of nulls. - The field carries
complexity: 10on bothGroupandProject. 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_hOn 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.