Loading
Suppress the Duo trial CTA on Dedicated
What does this MR do and why?
On GitLab Dedicated, the Duo Chat panel offered a "Start a Free Trial" CTA. Dedicated customers are already on Ultimate and are never eligible for an Ultimate trial. The trial path only branched on SaaS vs. self-managed and had no Dedicated guard.
- Adds that guard, so the trial CTA is not offered on Dedicated.
- Guarding alone is not enough:
DuoChatPanel::Component#component_instanceis an if/elsif chain that renders exactly one component, so removing the trial branch falls through toAccessDeniedComponent, whose copy tells the viewer to contact a project Maintainer or group Owner. The people who saw the trial CTA are admins and group Owners, so that message is aimed at the wrong person. - Adds
DuoChatPanel::DuoDisabledAdminComponent, which renders the existing "Turn on GitLab Duo Agent Platform" empty state with a link to Duo settings. This needs no frontend changes: that empty state already sits above the trial and access-denied branches in the panel's Vue blocked-state view, and it keys off thechat_disabled_reasonandduo_settings_pathdata attributes the new component emits. - Duo settings path building moves onto
DuoChatPanel::Container, where the path is nil unless the container is persisted, the user can administer it, and the container was not merely inferred from the user's default Duo namespace. That presence check doubles as the permission gate, so the state cannot render with a dead link. show_duo_disabled_admin?starts withreturn false if ::Gitlab::CurrentSettings.duo_never_on?. When an instance setsduo_availabilitytonever_on, Duo is off instance-wide and the group/project Duo settings page this empty state links to cannot turn it back on, so showing the panel would be pointless. Without the guard, a group Owner who is not an instance admin would newly see the "Turn on GitLab Duo Agent Platform" panel on such an instance, where previously they saw nothing, because the trial branch required the instance-admin-only:update_gitlab_subscriptionability and fell through to nil (access_denied?is!duo_never_on?). Caught in review by @arpitgogia; covered by a spec assertingcomponent_instanceis nil in this case.- Three configurations reach this branch on real Dedicated instances, not two: Duo disabled via the
duo_features_enabledsetting on the namespace, Duo enabled but the add-on not purchased, and Duo enabled with the add-on purchased but the individual viewer has no Duo seat assigned. The branch fires whenever the viewer has no Duo access anywhere (show_duo_entry_point?false), so it cannot distinguish between them, and the MR intentionally renders the same state for all three. It is correct and actionable in the first case. It is imprecise in the second, since the setting is already on. It is also imprecise in the third — probably the most common case on real Dedicated instances, since those customers are on Ultimate and typically hold the add-on already — where the copy points at a toggle that is already on. All three are still better than offering a trial that goes nowhere; disambiguating them is follow-up work in #618009. - Non-Dedicated behaviour is unchanged, covered by a regression test.
Screenshots or screen recordings
| Before | After |
|---|---|
![]() |
![]() |
How to set up and validate locally
- Use a self-managed GDK with an Ultimate license — not SaaS simulation mode. If the performance bar shows a "SaaS" badge, restart without SaaS simulation, because in SaaS mode the trial path takes a different branch and Dedicated is not applicable.
- Sign in as an admin who is Owner of a group and who has no GitLab Duo seat assigned. This is the default GDK state.
- Confirm the bug first. In a Rails console, set the instance to non-Dedicated:
Visit the group and open the Duo panel. It shows "Try GitLab Duo Agent Platform" with a "Start a Free Trial" button.
ApplicationSetting.current.update!(gitlab_dedicated_instance: false) Gitlab::CurrentSettings.expire_current_application_settings - Flip the instance to Dedicated:
The
ApplicationSetting.current.update!(gitlab_dedicated_instance: true) Gitlab::CurrentSettings.expire_current_application_settingsexpire_current_application_settingscall matters: application settings are cached, and without it the running web process keeps serving the old value. - Reload the group page and open the Duo panel. It now shows "Turn on GitLab Duo Agent Platform" with a "Go to Duo settings" button, and the button links to that namespace's Duo settings.
- The steps above reproduce the seat-unassigned case, since the default GDK admin has no Duo seat. Additionally running
group.namespace_settings.update!(duo_features_enabled: false)reproduces the toggle-off case; the panel shows the same state for both. - Restore afterwards: set
gitlab_dedicated_instanceback tofalse, andduo_features_enabledback totrueif it was changed. A lot of unrelated behaviour keys off the Dedicated flag.
Automated tests:
bundle exec rspec ee/spec/components/duo_chat_panel/
yarn jest ee/spec/frontend/ai/init_duo_panel_spec.jsReferences
- The issue: https://gitlab.com/gitlab-org/gitlab/-/issues/613020
- Follow-up: #618009 tracks disambiguating the three causes of the empty state (setting off, add-on not purchased, seat not assigned), plus the other gaps review surfaced: scope-less pages falling back to the access-denied state, locked subgroup/ancestor toggles, and migrating the panel off
chat_disabled_reasonto an explicit state flag.
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 Paul Gascou-Vaillancourt

