Foundational flows overload goal to pass resource identity - replace with structured resource pointer

Problem

Several GitLab Duo foundational flows smuggle a resource identifier into the free-text goal field of a workflow's StartWorkflowRequest, instead of passing it as structured data:

  • code_review/v1: ReviewMergeRequestService sets goal: merge_request.iid - a bare integer, not a natural-language instruction. The flow definition's MERGE_REQUEST_RESOLVER in code_review.rb then regex-parses goal back into a MergeRequest, accepting either a bare IID or a same-project MR URL.
  • secrets_fp_detection/v1: duo_workflow_helpers.rb does Vulnerability.find_by_id(params[:goal]), and the worker-triggered path sends user_prompt: vulnerability.id.to_s - the same pattern one layer up.

Why this is a problem

  • Conflates two different concerns: goal is meant to express what the agent should do; here it's instead (or additionally) expressing what resource it should act on. A flow that legitimately needs both a real instruction and a resource pointer has nowhere to put the resource pointer.
  • Fragile parsing: recovering the resource requires regex-matching a bare IID or reconstructing/matching a project URL. find_by_iid/find_by_id return nil silently on a bad match, and the MR-URL regex has to explicitly tolerate suffixes from browser-copied URLs (/diffs, ?query, #note anchors).
  • Doesn't generalize: every new flow that needs to identify a resource has to reinvent this, and the parsing logic is duplicated per flow / per resource type.

What already exists that we can build on

GitLab Rails already has a working, production-proven mechanism for sending structured metadata to Duo Workflow Service without any AI Gateway proto change: the additional_context envelope. A comment in StartWorkflowService documents the compatibility contract explicitly:

"Trigger metadata (how the flow was started) travels in its own envelope so the standard context schema stays frozen across deploys: a Duo Workflow Service that does not know this category skips the whole envelope with a warning, whereas an unknown field inside a known category fails envelope validation."

This is already exercised in production via the agent_platform_trigger_context category, and secrets_fp_detection/v1 already injects its own ad hoc secret_detection_context category (see duo_workflow_helpers.rb#L202-217) alongside the existing goal-overload hack.

Proposed solution

Add a resource_id concept, carried as a self-describing GraphQL-style GlobalID (e.g. "gid://gitlab/MergeRequest/123", "gid://gitlab/Vulnerability/456"), sent alongside, not instead of, the existing goal value.

Add a new reserved category, e.g. agent_platform_resource_context, with content: {"resource_id": "<gid>"}, following the exact pattern of agent_platform_standard_context/agent_platform_trigger_context. No AI Gateway proto change is required to start sending this; it relies on the documented "unknown category is skipped" behavior for old AI Gateway/Duo Workflow Service versions. AI Gateway would separately need to start reading this category for code_review/v1/secrets_fp_detection/v1 to actually benefit from it - that's a follow-up conversation with AI Gateway maintainers, ideally advertised via the existing ListCapabilities RPC so gitlab-rails can detect support rather than guess by version.

Option B - new field on StartWorkflowRequest

optional string resourceId = 14 [json_name = "resource_id"]; // field 7 stays reserved

More explicit/typed, but requires a proto change, gem regen, and version bump in the AI Gateway repo, then vendoring the updated gem into gitlab-rails, before Rails can use it at all - a cross-repo release dependency Option A avoids.

Either way, backward compatibility must be indefinite, not a short migration window: self-managed GitLab instances can lag SaaS by months to years, so AI Gateway's goal-parsing fallback for these flows can't be assumed removable on any fixed timeline - it should be treated like the already-permanent legacy-CLI fallback for workflowDefinition resolution in StartWorkflowService.

Edited by Wanderson Policarpo