Introduce `duo_code_review_flow_priority` feature flag and update `Modes::Dap#active?` to route Duo Enterprise users through DAP when consent is given
## Summary Introduce the `duo_code_review_flow_priority` feature flag and update `Ai::DuoCodeReview::Modes::Dap#active?` so that Duo Enterprise users are routed through DAP when the new feature flag is enabled **and** their group has explicitly consented (via the `group_consents` table introduced in #602688). The existing `duo_code_review_dap_internal_users` override is preserved as a fast-path that bypasses the consent requirement. This is part of epic [&22247](https://gitlab.com/groups/gitlab-org/-/work_items/22247). **Depends on:** #602688 (`group_consents` table and `Groups::Consent` model must exist first). --- ## Why Today, `Modes::Dap#active?` short-circuits for any user with a Duo Enterprise add-on: ```ruby # Current behaviour return ::Feature.enabled?(:duo_code_review_dap_internal_users, user) if user_has_duo_enterprise_add_on? ``` This means Code Review Flow being enabled for the namespace is completely ignored for Duo Enterprise users — they always fall through to `Modes::Classic`. The fix inverts the priority: if the new feature flag is on **and** the group has consented, DAP wins regardless of Duo Enterprise seat assignment. The `duo_code_review_dap_internal_users` flag is kept as an explicit override for GitLab-internal users, allowing us to test the full flow before rolling out to customers. --- ## What ### 1. Feature flag Add `ee/config/feature_flags/ops/duo_code_review_flow_priority.yml`: ```yaml --- name: duo_code_review_flow_priority description: > When enabled, the Code Review Flow toggle and group consent become the authoritative routing signal for Duo Code Review, taking priority over Duo Enterprise seat assignment. Requires explicit group consent recorded in group_consents (feature_name: code_review_flow_dap_routing). feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/<this_issue> introduced_by_url: # filled in by MR rollout_issue_url: # to be created milestone: '19.2' group: group::ai coding type: ops default_enabled: false ``` ### 2. Updated `Modes::Dap#active?` logic The current Duo Enterprise branch is a single early return: ```ruby # Before return ::Feature.enabled?(:duo_code_review_dap_internal_users, user) if user_has_duo_enterprise_add_on? ``` It becomes a structured block with three explicit paths: ```ruby # After if user_has_duo_enterprise_add_on? # Fast-path override: internal GitLab users bypass the consent requirement. # Preserves existing behaviour — no change for users covered by this flag. return true if ::Feature.enabled?(:duo_code_review_dap_internal_users, user) # New path: route through DAP if the feature flag is on AND the group has # explicitly consented to DAP routing (recorded in group_consents). if ::Feature.enabled?(:duo_code_review_flow_priority) return container.root_ancestor.consented_to?(:code_review_flow_dap_routing) && container.duo_code_review_dap_available? && duo_agent_platform_configured? end # Default: Duo Enterprise without the new flag → not active, falls through to Classic. return false end ``` The non-Duo-Enterprise branch (Duo Pro/Core) is **unchanged**. **Decision table for Duo Enterprise users after this change:** | `duo_code_review_dap_internal_users` | `duo_code_review_flow_priority` | Group consented? | Result | |---|---|---|---| | enabled (for user) | any | any | ✅ DAP (existing override, unchanged) | | disabled | enabled | yes | ✅ DAP (new path) | | disabled | enabled | no | ❌ Classic | | disabled | disabled | any | ❌ Classic (existing behaviour, unchanged) | --- ## How 1. Add `ee/config/feature_flags/ops/duo_code_review_flow_priority.yml`. 2. Update `active?` in `ee/app/models/ai/duo_code_review/modes/dap.rb` as described above. 3. Update `ee/spec/models/ai/duo_code_review/modes/dap_spec.rb` to cover all new branches inside the `when user has duo_enterprise add-on` context: - `duo_code_review_dap_internal_users` enabled → active (existing spec, verify it still passes). - `duo_code_review_dap_internal_users` disabled + `duo_code_review_flow_priority` enabled + group consented → active. - `duo_code_review_dap_internal_users` disabled + `duo_code_review_flow_priority` enabled + group **not** consented → not active. - `duo_code_review_dap_internal_users` disabled + `duo_code_review_flow_priority` disabled → not active (existing behaviour). 4. Verify all existing non-Duo-Enterprise specs still pass without modification. --- ## Acceptance criteria - [ ] `duo_code_review_flow_priority` feature flag exists, `default_enabled: false`, type `ops`. - [ ] When `duo_code_review_dap_internal_users` is enabled for a Duo Enterprise user, `active?` returns `true` regardless of consent or the new flag (existing override preserved). - [ ] When `duo_code_review_flow_priority` is enabled and the group has a `Groups::Consent` record for `:code_review_flow_dap_routing`, `active?` returns `true` for Duo Enterprise users (subject to `duo_code_review_dap_available?` and `duo_agent_platform_configured?`). - [ ] When `duo_code_review_flow_priority` is enabled but the group has **not** consented, `active?` returns `false` for Duo Enterprise users (falls through to Classic). - [ ] When `duo_code_review_flow_priority` is disabled, Duo Enterprise behaviour is identical to today (no regression). - [ ] Non-Duo-Enterprise (Duo Pro/Core) routing is completely unchanged. - [ ] All new branches are covered by unit tests in `dap_spec.rb`. --- ## Out of scope - Writing the consent record from the UI (Issues 4 & 5). - Any frontend changes. - Changes to `Modes::Classic` or `ModeResolver`.
issue