Add duo_auto_mode cascading setting implementation behind duo_auto_mode flag

What does this MR do and why?

This MR introduces a duo_auto_mode feature flag as well as the cascading setting logic for auto mode to be enabled at the namespace/group/project instance. The UI for this setting will be handled in the next MR: Add duo_auto_mode UI toggle to Duo settings (!253317 - merged) (which was the original MR that has now been split up).

The DB changes were included in this MR: Add duo_auto_mode_enabled cascading setting dat... (!255428 - merged).

It's important to note this thread for implementation details pertaining to read/write cascading settings. This setting is a read cascade setting which normally don't have defaults, but that is now a legacy pattern. While a fix is being created, the work around is to include a reset_duo_auto_mode_to_inherit_from_namespace method which is in create-service.rb. Here is the MR to close this gap in the future: Refactor duo cascading defaults into a single d... (!255492 - merged).

References

Partially closes: Auto mode: duo_auto_mode cascading setting, cap... (#618088 - closed).

Related to auto mode 🚀

Screenshots or screen recordings

Before After

How to set up and validate locally

How to set up and validate locally 🤖 Generated (GDK)

Prerequisites

  • A GDK running EE (all the code is under ee/).
  • A license with AI features (the REST direct_access path needs ai_workflows + duo_features_enabled; the project mutation needs an active Duo add-on or SaaS emulation).
  • duo_auto_mode is default_enabled: false, so it is off on a GDK until you enable it.
  • The Duo Workflow Service does not need to be running: duo_auto_mode is a Rails-computed capability appended locally.

Out of scope: this branch also touches ee/app/services/ai/tool_rules/resolution_service.rb / ee/lib/ai/tool_rules/permissions.rb (a local_surface? governance relaxation gated by the separate duo_workflow_local_tool_governance flag). That is a separate concern and is not part of validating duo_auto_mode.

1. Enable the feature flag

# Rails console. The flag is checked against `project || namespace`.
Feature.enable(:duo_auto_mode)            # instance-wide (simplest)
Feature.enable(:duo_auto_mode, group)     # or per-actor
Feature.enable(:duo_auto_mode, project)

In RSpec, WIP flags default to enabled, which is why the specs assert the capability without enabling it. On a GDK you must enable it explicitly.

2. Set the cascading setting

duo_auto_mode_enabled (+ lock_duo_auto_mode_enabled) is a cascading_attr at instance/group/project, with a three-state duo_auto_mode_availability accessor (default_on / default_off / never_on).

# Instance (ApplicationSetting)
s = ApplicationSetting.current
s.duo_auto_mode_availability = :default_on   # duo_auto_mode_enabled=true, lock=false
s.save!

# Group (namespace_settings)
group = Group.find_by_full_path('my-group')
group.namespace_settings.update!(duo_auto_mode_enabled: true)
group.namespace_settings.duo_auto_mode_enabled?          # cascading reader (walks ancestors + instance)
group.namespace_settings.duo_auto_mode_enabled_locked?

# Project (project_settings)
project = Project.find_by_full_path('my-group/my-project')
project.project_setting.update!(duo_auto_mode_enabled: true)
project.project_setting.duo_auto_mode_enabled?

# Effective value with project-wins precedence
Ai::DuoSettings::CascadingSettingResolver.enabled?(project: project, namespace: group, &:duo_auto_mode_enabled?)

3. Verify the capability is advertised

The duo_auto_mode capability is advertised in two mirrored places, both gated on flag AND effective setting: GraphQL aiFlowsMetadata (Ai::FlowsMetadataService) and REST direct_access (Api::Ai::DuoWorkflows::Workflows#compute_server_capabilities).

# http://gdk.test:3000/-/graphql-explorer  (use projectId to test project scope; project wins when both are given)
query {
  aiFlowsMetadata(namespaceId: "gid://gitlab/Namespace/123") {
    capabilities { name metadata }
  }
}

With flag + setting ON, duo_auto_mode appears in capabilities; with the flag off, the setting off/never_on, or no setting in scope, the duo_auto_mode entry is simply absent.

# REST direct_access (token needs ai_workflows scope) -> server_capabilities includes "duo_auto_mode"
curl --request POST --header "PRIVATE-TOKEN: <token>" \
  "http://gdk.test:3000/api/v4/ai/duo_workflows/direct_access?namespace_id=123"

4. Verify write-cascade + new-project inheritance

# Group -> descendants cascade (Namespaces::CascadeDuoSettingsWorker)
group   = Group.find_by_full_path('my-group')
project = Project.find_by_full_path('my-group/my-project')
Sidekiq::Testing.inline! do
  Groups::UpdateService.new(group, group.owners.first, { duo_auto_mode_enabled: true }).execute
end
project.project_setting.reload.duo_auto_mode_enabled     # => true (cascaded down)

# New project inherits from its group (reset_duo_auto_mode_to_inherit_from_namespace)
group.namespace_settings.update!(duo_auto_mode_enabled: true)
p = Projects::CreateService.new(group.owners.first,
      { namespace_id: group.id, name: 'inherit-check', path: 'inherit-check' }).execute
p.project_setting.duo_auto_mode_enabled                  # => true (inherited on create)

Instance-level changes cascade via AppConfig::CascadeDuoSettingsWorker.

5. Specs

bin/rspec ee/spec/models/namespace_setting_spec.rb ee/spec/models/ee/project_setting_spec.rb ee/spec/models/application_setting_spec.rb
bin/rspec ee/spec/services/ai/flows_metadata_service_spec.rb ee/spec/services/ai/cascade_duo_settings_service_spec.rb
bin/rspec ee/spec/requests/api/ai/duo_workflows/workflows_spec.rb ee/spec/requests/api/ai/duo_workflows/cascading_settings_spec.rb
bin/rspec ee/spec/graphql/ee/mutations/groups/update_spec.rb
# add `-e "duo_auto_mode"` to target only the relevant examples

No jest is required for this MR (backend-only slice).

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 Dylan Bernardi

Merge request reports

Loading
Loading