Track namespace-level agent sessions with their namespace

What does this MR do and why?

This MR passes the workflow's namespace to track_internal_event in track_workflow_event, so the six agent_platform_session_* events for a namespace-level Duo Agent Platform session carry that namespace. Until now they carried none. Events already recorded keep no namespace. CleanStuckWorkflowsService already makes the same call for its cleanup event.

Why the namespace is missing

Every workflow has exactly one of project_id and namespace_id (check_73884a5839 on duo_workflows_workflows), and track_workflow_event passed only the user and the project. With no project, neither Gitlab::InternalEvents nor Gitlab::Tracking::AiTracking can resolve a namespace. The Snowplow context has no namespace_id. The PostgreSQL ai_usage_events row has a NULL namespace_id, and the ClickHouse row has no namespace in namespace_path or traversal_path. Both Ai::UsageEventsFinder backends, which serve Resolvers::Analytics::AiUsage::UsageEventsResolver, filter a group's events on those columns (namespace_id and traversal_path), so these sessions never match.

Event path for a namespace-level session

sequenceDiagram
    participant S as Ai::DuoWorkflows services
    participant T as track_workflow_event
    participant IE as Gitlab::InternalEvents
    participant AT as Gitlab::Tracking::AiTracking
    participant UE as Ai::UsageEvent
    S->>T: agent_platform_session_* event
    T->>IE: track_internal_event(user:, project:, namespace:)
    Note over T,IE: Before: project is nil, no namespace passed<br/>After: namespace is workflow.namespace
    IE->>IE: Snowplow and Redis: namespace, else project.namespace
    IE->>AT: track_event with the caller's arguments
    AT->>UE: project namespace first, else namespace
    Note over IE,UE: Before: no namespace in Snowplow or ai_usage_events<br/>After: the session's namespace in both

Project-level sessions are unchanged

A project-level workflow has no namespace_id, so it passes namespace: nil. Gitlab::InternalEvents still derives the namespace from the project, and Gitlab::Tracking::AiTracking still takes the project's project_namespace first, so these events record the same namespace as before.

The cleanup spec

@pgumeson recorded this gap in !249723 (merged) and asserted it in clean_stuck_workflows_service_spec.rb so it would stay visible. That example now expects the namespace, and a new spec for the concern covers a project-level and a namespace-level workflow.

References

How to set up and validate locally

  1. Run the new concern spec, the cleanup spec and the three other caller specs:

    bundle exec rspec ee/spec/services/ai/duo_workflows/concerns/workflow_event_tracking_spec.rb \
      ee/spec/services/ai/duo_workflows/clean_stuck_workflows_service_spec.rb \
      ee/spec/services/ai/duo_workflows/create_workflow_service_spec.rb \
      ee/spec/services/ai/duo_workflows/update_workflow_status_service_spec.rb \
      ee/spec/services/ai/duo_workflows/create_and_start_workflow_service_spec.rb

    Ran locally with this change: 327 examples, 0 failures, 1 pending (already pending on master). RuboCop reports no offenses on the three changed files.

  2. Revert the one-line change in workflow_event_tracking.rb and rerun the first two specs. Ran locally: 37 examples, 8 failures. The failures are the concern spec's seven namespace-level examples and the cleanup spec's namespace-level dropped example, and the project-level example still passes.

  3. On a group's page, start a chat with Add new chat on the GitLab Duo sidebar, then query the group's aiUsageData in GraphQL as a group Owner. Tested on a self-managed GitLab 19.4.1 instance with a stub model behind a self-hosted AI gateway, with sessions started from the web UI on a group and on one of its projects. 19.4.1's copy of the concern lacks the agent configuration properties on master, so only the namespace: workflow.namespace line was ported to it.

    • Group-level sessions: the ai_usage_events rows for created, started, resumed (a second prompt), stopped (Cancel session) and dropped (a prompt sent while the model was unreachable) carry the group's namespace_id, and the Owner's aiUsageData read returns them. Without the change, namespace_id is NULL and the read omits them.
    • Project-level rows still carry the project's project_namespace, as they did without the change for created and started.
    • A group-level session cannot reach finished on 19.4.1, so that event was not tested by hand at group level. The concern spec covers all six events for a namespace-level workflow.

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

This MR was prepared with assistance from Claude Code.

Edited by Andrew Dunn

Merge request reports

Loading
Loading