Add CLI client and endpoint properties to glab event

What does this MR do and why?

Declares six additional properties on the gitlab_cli_command_used event so the GitLab CLI can report which client is driving it and, for glab api, which endpoint was called.

Property Example Why
coding_agent claude-code Split CLI usage by AI coding agent vs human
cli_version v1.116.0 Which versions are still in the wild
platform darwin Platform mix
architecture arm64 Architecture mix
api_endpoint /projects/{}/merge_requests What glab api actually calls
http_method GET Separate read from write traffic

Two questions are currently unanswerable from the warehouse:

  • How much CLI usage is agent-driven. glab already detects the driving agent and appends Coding-Agent/<name> to the outbound HTTP User-Agent. That reaches API request logs, which are not joinable to this event stream in the warehouse. CLIENT_NAME and AGENT_NAME are 100% NULL across all gitlab_cli_command_used rows. This is the question the DAP adoption dashboards are being asked, and the CLI is collecting the answer and discarding it.
  • What glab api calls. It is roughly a third of CLI events and growing (5.3% of volume in January 2026, 35.1% in August). It takes its endpoint as an argument rather than a subcommand, so EVENT_PROPERTY is NULL and the event records only that someone called the API.

Why this declaration is required

EventsRouter#public_additional_properties slices additional_properties down to the keys declared in the event definition:

def public_additional_properties
  public_properties = event_definition.additional_properties.keys
  additional_properties.slice(*public_properties)
end

That runs before track_analytics_event splits the hash into the reserved label/property/value columns and the free-form extra. So undeclared properties are dropped rather than passed through to gsc_extra, and the CLI cannot ship these until they are declared here.

Scope

This declaration is the only change needed. The values ride in the existing gitlab_standard context's extra field and land in gsc_extra, which already carries command_and_subcommand through to Snowflake intact. There is no Iglu schema version bump, no collector change, and no new mart column — the dashboard reads them with a JSON path.

Privacy

All six are low cardinality and none carry user or project identifiers.

api_endpoint is the one worth scrutinising. Raw API paths carry project IDs, usernames, branch names and occasionally tokens in query strings, so they are never sent. The CLI matches the request path against a route table generated from client-go's route templates and reports the matching template. An identifier is not distinguishable from a route noun by shape — users/phikai/projects looks exactly like users/keys/1 — so heuristic templating is not used. A path with no matching route is reported as the literal unmatched rather than partially redacted, which bounds cardinality and makes leakage structurally impossible: every emitted value is either a literal from the table or the sentinel.

coding_agent is omitted rather than sent empty when no agent is detected, so "not agent-driven" stays distinguishable from "client too old to populate the field".

The CLI merge requests can merge before this one. Until this deploys the properties are simply sliced off, which is harmless; they start being retained once it ships.

MR acceptance checklist

  • Followed the Internal Analytics event definition conventions
  • spec/lib/gitlab/internal_events/event_definitions_spec.rb passes locally
  • Reviewed by the Analytics Instrumentation team

Merge request reports

Loading
Loading