Honour foundational flow agent_privileges on trigger/Slack path

What does this MR do and why?

Closes: https://gitlab.com/gitlab-org/gitlab/-/work_items/629878 Closes: #630334 (closed)

A Duo foundational flow declares the permissions it needs in its definition — read files, write to GitLab, run shell commands. But flows started through Ai::Catalog::ExecuteWorkflowService (issue/MR triggers, @GitLabDuo mentions, CI-runtime flows started from Slack, the vulnerability and dependency-bump workers) ignored that declaration and received a fixed, broader permission set instead.

The visible case is Duo Developer: its definition does not declare shell access, yet trigger-started runs could shell out anyway — while the same flow started through other paths could not. The declared permissions did not describe what the flow could actually do, and the mismatch failed silently in both directions.

This MR makes that path honour the definition:

  1. Ai::Catalog::ExecuteWorkflowService resolves both agent_privileges and pre_approved_agent_privileges from the foundational flow definition, so a flow that pre-approves less than it grants keeps that narrowing, as it already does on the other start paths. The hardcoded constant stays only as the fallback for custom catalog flows, which have no definition.

  2. Definitions now declare exactly what their AI Gateway flow configs use:

    • developer: adds run_commands.
    • convert_to_gl_ci: adds run_commands and read_only_files.
    • security_review and risk_classification: add read_only_files and read_only_gitlab (their configs call read_file/find_files), and drop run_commands and use_git (their configs never call run_command/run_git_command).

    This must land together with (1) — otherwise honouring the definition would remove tools from working flows.

  3. post_suggested_reviewers (used by the Recommend Reviewers flow) is catalogued and mapped to the read_write_gitlab privilege group, so namespace tool rules can govern it. It previously had no catalog entry, so a rule could not attach to it.

Behaviour change

Foundational flows on this path no longer receive run_mcp_tools or start_flows unless their definition declares them. This is intended: no foundational flow config uses MCP tools, and only slack_assistant and workplan declare start_flows. Previously run_mcp_tools pre-approved every MCP tool for unattended runs.

Risk

Privileges live in this repository, while the toolsets that consume them live in the AI Gateway flow configs. If a flow's declared privileges miss a tool its config uses, the gateway drops that tool silently. To cover this, every foundational flow's declared privileges were compared against its gateway config, and most flows were run end to end locally (see validation below). Follow-ups to make this mismatch visible are discussed in the MR thread.

References

Screenshots or screen recordings

No UI changes. Behaviour change is visible in the session's granted privileges.

Before After
Trigger-started developer/v1 session has privileges [1, 2, 3, 4, 5, 6, 7] (full hardcoded set) Trigger-started developer/v1 session has privileges [1, 2, 3, 4, 5] (the declared set, including run_commands)

How to set up and validate locally

Requires a GDK with the AI Gateway and Duo features enabled on a test project (for example the seeded gitlab-duo/test), with the flows below enabled. security_review and risk_classification are Ultimate-only; risk_classification also needs Feature.enable(:duo_mr_risk_classification).

For each flow, trigger it, then check the recorded privileges and the final status in the Rails console:

Ai::DuoWorkflows::Workflow.order(:id).last.slice(:id, :workflow_definition, :agent_privileges, :pre_approved_agent_privileges, :status)

Then compare the tools each LLM call received in LangSmith with the toolset: of that component in the AI Gateway config. Every configured tool must be present.

Flow How to trigger Expected agent_privileges
developer/v1 Mention or assign the Developer service account on an issue [1, 2, 3, 4, 5]
fix_pipeline/v1 Fail a CI pipeline in the test project [1, 2, 3, 4, 5]
convert_to_gl_ci/v1 Open a Jenkinsfile and use the convert action in the file header [8, 1, 2, 3, 4, 5]
risk_classification/v1 Open a new merge request [8, 2, 3]
security_review/v1 Assign the Security Review service account as reviewer, or mention it on an MR [8, 2, 3]
resolve_dependency_bump/experimental Mention it on a dependency update MR [1, 2, 3, 4, 5]
resolve_sast_vulnerability/v1 Start a resolution from a SAST vulnerability [1, 2, 3, 4, 5]

All runs should finish with status 3 (finished). None should record 6 (run_mcp_tools) or stop in 8 (tool_call_approval_required).

Custom catalog flows still get the full set (regression guard): trigger any custom (non-foundational) catalog flow and confirm agent_privileges is [1, 2, 3, 4, 5, 6, 7].

post_suggested_reviewers is governable (optional): with duo_workflow_background_tool_governance enabled for the group, configure an Ai::ToolRule denying post_suggested_reviewers, run the Recommend Reviewers flow on an MR, and confirm the tool is denied.

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.

Edited by Thomas Schmidt

Merge request reports

Loading
Loading