Add per-flow coding_environment declaration for Duo Agent Platform flows

What does this MR do and why?

Introduces an optional coding_environment property that a Duo Agent Platform flow can declare to control what kind of coding environment GitLab prepares before the workload starts.

Background: dap_full_clone applies a full blobless clone to all DAP flows. API-only flows pay that cost for nothing. Each flow can now declare what it needs; the safe default is the full environment.

Allowed values

Value Behaviour
full Default when absent. Repository clone, setup scripts, Git hooks, and dependency cache.
none No clone (GIT_STRATEGY=none). Setup scripts, Git hooks, and dependency cache are all skipped.

A value other than full or none is rejected by flow_v2.json at save time, so a typo never reaches the resolver. An absent declaration resolves to full, so a missing declaration can never silently remove a flow's repository access.

Resolution order

Ai::Catalog::CodingEnvironment.resolve consults, in order:

  1. The coding_environment key of a catalog/custom flow's flow_config
  2. The coding_environment attribute of the Ai::Catalog::FoundationalFlow registry entry
  3. :full

Fall-through is driven by the absence of a value, not the absence of a flow_config Hash. A config that declares nothing (or declares it under a Symbol key) still reaches the registry, so one source cannot silently mask the other.

Relationship to dap_full_clone

None — deliberately. dap_full_clone selects how to clone; coding_environment selects whether to clone. Under GIT_STRATEGY=none the depth, filter and LFS variables are inert, so the legacy clone path needs no protection and the declaration behaves identically on both sides of that flag.

Gating this behind dap_full_clone would have made a documented, schema-validated YAML property a silent no-op wherever the flag is off (it is default_enabled: false), and tied two unrelated rollouts to one kill switch.

Files changed

  • ee/app/models/ai/catalog/coding_environment.rb — new value object owning resolution
  • ee/app/models/ai/catalog/foundational_flow/attributes.rbcoding_environment attribute, default 'full'
  • app/validators/json_schemas/ai_catalog/flow_v2.json — optional string property, enum full / none
  • ee/app/services/ai/duo_workflows/start_workflow_service.rb — apply the resolved environment to the workload definition
  • Specs for all three layers, plus a guardrail lint on Items.fixed_items
  • Docs: custom_flows_schema.md

References

Related to #606480 (closed)

The lightweight value is reserved for a future mechanism being decided in !247414 (closed).

Screenshots or screen recordings

N/A — backend only, no UI.

How to set up and validate locally

The signal is in the Git phase of the workload job log, before the duo CLI starts. The flow does not need to succeed for the test to be valid.

1. Custom flow (the flow_config branch)

Create a flow at <project>/-/automate/flows/new with this definition:

version: "v1"
environment: ambient

# Declares that this flow needs no coding environment.
coding_environment: none

components:
  - name: "api_agent"
    type: AgentComponent
    prompt_id: "api_prompt"
    inputs:
      - "context:goal"
    toolset: []
    ui_log_events:
      - "on_agent_final_answer"

prompts:
  - prompt_id: "api_prompt"
    name: "API Only Test Prompt"
    prompt_template:
      system: |
        You are a test agent. You verify that a GitLab Duo flow can run
        without a repository checkout. Answer in one short sentence.
      user: |
        {{goal}}
      placeholder: history
    unit_primitives: []
    params:
      timeout: 180

routers:
  - from: "api_agent"
    to: "end"

flow:
  entry_point: "api_agent"

Do not add yaml_definition — the server injects it.

Enable the flow on the project and trigger it (for example, mention the flow's service account in an issue comment). Then open the newest workload job.

Expect:

  • Log contains Skipping Git repository setup and Skipping Git checkout
  • Log does not contain Fetching changes or Checking out ... as detached HEAD
  • Script contains no git remote set-url origin, git checkout -B, or git fetch --no-tags

Remove the coding_environment: none line, re-run, and confirm the clone returns.

2. Foundational flow (the registry branch)

Temporarily add coding_environment: "none" to any entry in ee/app/models/ai/catalog/foundational_flow/items.rb, then restart so the class-level fixed_items cache is rebuilt:

gdk restart rails-web rails-background-jobs

Trigger that flow and check the same markers.

Note: flows holding USE_GIT or READ_WRITE_FILES will fail the guardrail spec in foundational_flow_spec.rb while this edit is applied. That is the spec doing its job.

3. Independence from dap_full_clone

Repeat either test with the flag off. The result must be identical:

bin/rails runner 'Feature.disable(:dap_full_clone)'

4. Inspecting a job without reading the log

bin/rails runner '
b = Ci::Build.find(<JOB_ID>)
vars = (b.yaml_variables || []).to_h { |v| [v[:key], v[:value]] }
%w[GIT_STRATEGY GIT_DEPTH GIT_FETCH_EXTRA_FLAGS GIT_LFS_SKIP_SMUDGE].each do |k|
  puts format("%-22s %s", k, vars.key?(k) ? vars[k].inspect : "(absent)")
end
puts "script_lines: #{Array(b.options.dig(:script)).size}"
'

5. Schema validation

Saving a flow with coding_environment: lightweight must fail with:

value at `/coding_environment` is not one of: ["full", "none"]

Validation results

Verified on GDK across five workload jobs. All five completed successfully.

Source Declared dap_full_clone GIT_STRATEGY Clone vars Script lines Runner
foundational absent on absent DEPTH=0, blob:none, LFS=1 58 Checking out
foundational none on none absent 50 Skipping Git repository setup
foundational none off none absent 50 Skipping Git repository setup
custom flow none on none absent 50 Skipping Git repository setup
custom flow absent on absent DEPTH=0, blob:none, LFS=1 58 Checking out

The 58 → 50 script delta is exactly coding_environment_commands: four Git-hook lines, one OAuth remote, three workspace fixups.

Not covered by these runs: the dependency cache skip. The test project configures no cache, so d.cache was nil throughout. That branch is covered by specs only.

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