Add GLQL presenters for merge request and Duo usage analytics

What does this MR do and why?

Frontend half of enabling the two new GLQL analytics sources (MergeRequest and AiUsageEvent, added to the compiler in 0.33.0). Bumps @gitlab/query-language-rust to 0.33.0 and registers presenters for the new fields:

  • throughputCount, featuresCount, returningUsersCount, previousPeriodUsersCount render as numbers
  • timeToMergeQuantile gets a new millisecond-aware duration presenter. The merge request engine emits milliseconds (merge_duration_ms), unlike pipeline analytics where durationQuantile is seconds, so reusing the existing presenter would render values 1000x too large
  • user on Duo usage dimensions renders as a user avatar
  • state on merge request analytics dimensions now maps to the merge request badge shapes, with a translated Locked label and a neutral-badge fallback for unmapped states (previously an empty badge)

The compiler bump also refreshes parser spec snapshots (compile output gained field and _internal keys since 0.31.0).

The backend half (gem bump and request specs) is in Add merge request and Duo usage GLQL analytics ... (!248034 - merged). Neither source is documented until both are merged; docs are tracked in #592280 (closed) and #592272 (closed).

Author note (non-blocking): this will conflict trivially with !241834 (merged) (package version line and parser snapshots). I'm holding the merge order myself.

Screenshots

AiUsageEvent

GLQL query Display Type Screenshot
display: table
mode: analytics
query: type = AiUsageEvent and group = "gitlab-org" and timestamp > -30d
dimensions: feature, event, user, timestamp
metrics: totalCount, usersCount, featuresCount, returningUsersCount, previousPeriodUsersCount

table

Google_Chrome_Beta_2026-08-04_15.02.13

display: list
mode: analytics
query: type = AiUsageEvent and group = "gitlab-org" and timestamp > -30d
dimensions: feature, event, user, timestamp
metrics: totalCount, usersCount, featuresCount, returningUsersCount, previousPeriodUsersCount

list

Google_Chrome_Beta_2026-08-04_15.02.24

display: orderedList
mode: analytics
query: type = AiUsageEvent and group = "gitlab-org" and timestamp > -30d
dimensions: feature, event, user, timestamp
metrics: totalCount, usersCount, featuresCount, returningUsersCount, previousPeriodUsersCount

orderedList

Google_Chrome_Beta_2026-08-04_15.02.53

display: barChart
mode: analytics
query: type = AiUsageEvent and group = "gitlab-org" and timestamp > -30d
dimensions: feature
metrics: totalCount, usersCount, featuresCount

barChart

Google_Chrome_Beta_2026-08-04_15.03.17

display: columnChart
mode: analytics
query: type = AiUsageEvent and group = "gitlab-org" and timestamp > -30d
dimensions: feature
metrics: totalCount, usersCount, featuresCount

columnChart

Google_Chrome_Beta_2026-08-04_15.03.28

display: lineChart
mode: analytics
query: type = AiUsageEvent and group = "gitlab-org" and timestamp > -30d
dimensions: feature
metrics: totalCount, usersCount, featuresCount

lineChart

Google_Chrome_Beta_2026-08-04_15.03.40

display: areaChart
mode: analytics
query: type = AiUsageEvent and group = "gitlab-org" and timestamp > -30d
dimensions: feature
metrics: totalCount, usersCount, featuresCount

areaChart

Google_Chrome_Beta_2026-08-04_15.03.52

display: stat
mode: analytics
query: type = AiUsageEvent and group = "gitlab-org" and timestamp > -30d
metrics: featuresCount

stat

image

MergeRequest (analytics)

GLQL query Display Type Screenshot
display: table
mode: analytics
query: type = MergeRequest and group = "halcyon-software"
dimensions: created, merged, state, targetBranch
metrics: totalCount, throughputCount, timetoMergeQuantile

table

Google_Chrome_Beta_2026-08-05_09.55.54

display: list
mode: analytics
query: type = MergeRequest and group = "halcyon-software"
dimensions: created, merged, state, targetBranch
metrics: totalCount, throughputCount, timetoMergeQuantile

list

Google_Chrome_Beta_2026-08-05_09.56.12

display: orderedList
mode: analytics
query: type = MergeRequest and group = "halcyon-software"
dimensions: created, merged, state, targetBranch
metrics: totalCount, throughputCount, timetoMergeQuantile

orderedList

Google_Chrome_Beta_2026-08-05_09.56.25

display: barChart
mode: analytics
query: type = MergeRequest and group = "halcyon-software" and created > -30d
dimensions: created, merged
metrics: timetoMergeQuantile(0.95)

barChart

Google_Chrome_Beta_2026-08-05_09.56.38

display: columnChart
mode: analytics
query: type = MergeRequest and group = "halcyon-software" and created > -30d
dimensions: merged
metrics: totalCount, timetoMergeQuantile(0.95)

columnChart

Google_Chrome_Beta_2026-08-05_09.56.56

display: lineChart
mode: analytics
query: type = MergeRequest and group = "halcyon-software" and created > -30d
dimensions: merged
metrics: totalCount, timetoMergeQuantile(0.95)

lineChart

Google_Chrome_Beta_2026-08-05_09.57.06

display: areaChart
mode: analytics
query: type = MergeRequest and group = "halcyon-software" and created > -30d
dimensions: merged
metrics: totalCount, timetoMergeQuantile(0.95)

areaChart

Google_Chrome_Beta_2026-08-05_09.57.21

display: stat
mode: analytics
query: type = MergeRequest and group = "halcyon-software" and created > -30d
metrics: totalCount

stat

Google_Chrome_Beta_2026-08-05_09.57.31

How to set up and validate locally

  1. Set up Clickhouse in your GDK

  2. Set up Siphon in your GDK so merge requests replicate into ClickHouse

  3. Enable Clickhouse-based analytics: echo 'Gitlab::CurrentSettings.current_application_settings.update(use_clickhouse_for_analytics: true)' | rails c

  4. Seed AI usage events: FILTER=ai_usage_stats bundle exec rake db:seed_fu (seeds the first project; use PROJECT_ID=<id> to target another)

  5. Seed MR Analytics: FILTER=productivity_analytics SEED_PRODUCTIVITY_ANALYTICS=1bundle exec rake db:seed_fu (seeds the first project; use PROJECT_ID=<id> to target another)

  6. Add a GLQL block to any issue or wiki page for merge request analytics:

    ```glql
    display: table
    mode: analytics
    query: type = MergeRequest and group = "gitlab-org" and merged > -30d
    dimensions: merged(weekly), state
    metrics: totalCount, throughputCount, timeToMergeQuantile(0.5)
    ```

    The table should render state values as merge request badges (including merged), and time to merge as a formatted duration (for example 1d 2h), not a raw millisecond count.

  7. And one for AI usage events:

    ```glql
    display: table
    mode: analytics
    query: type = AiUsageEvent and group = "gitlab-org" and timestamp > -30d
    dimensions: feature, user, timestamp
    metrics: totalCount, usersCount, featuresCount
    ```

    The user column should render avatars, and the metric columns plain numbers. No console errors in either case.

Related to #592279 (closed) and #592271 (closed).

Edited by Robert Hunt

Merge request reports

Loading
Loading