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.
glabalready detects the driving agent and appendsCoding-Agent/<name>to the outbound HTTPUser-Agent. That reaches API request logs, which are not joinable to this event stream in the warehouse.CLIENT_NAMEandAGENT_NAMEare 100% NULL across allgitlab_cli_command_usedrows. This is the question the DAP adoption dashboards are being asked, and the CLI is collecting the answer and discarding it. - What
glab apicalls. 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, soEVENT_PROPERTYis 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)
endThat 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".
Related
- gitlab-org/cli#8527 —
glab apiendpoint and method - gitlab-org/cli#8528 — coding agent, CLI version, platform
- CLI side: cli!3823 (merged) and cli!3824 (merged)
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.rbpasses locally - Reviewed by the Analytics Instrumentation team