Wire Inbox Session Design to Duo Experimental Setting

Problem

The redesigned session inbox is gated only by the duo_panel_session_inbox WIP flag. getNamespaceIndexComponent in ee/app/assets/javascripts/ai/duo_agents_platform/router/utils.js reads window.gon?.features?.duoPanelSessionInbox and picks either AgentSessionInbox or userAgentsPlatformIndex. A chatops percentage rollout is the only lever, so nobody outside the actors we name in #production can turn it on.

The epic (&23179 (closed)) ships to Customer Zero first and then promotes to Experiment or Beta. Promotion means the Duo "Experiment and beta features" setting decides who sees it — NamespaceSetting#experiment_features_enabled on .com, Gitlab::CurrentSettings.instance_level_ai_beta_features_enabled? on self-managed. The inbox reads neither today.

Gitlab::Llm::StageCheck.available? will not do it for us: it consults those settings only when the feature's maturity is :experimental or :beta, and duo_workflow, duo_agent_platform and foundational_flows are all maturity: :ga in ee/lib/gitlab/llm/utils/ai_features_catalogue.rb. Ai::DuoAgentPlatform::AccessPolicy already passes with the setting off.

Proposal

Inject the setting at the same place the flag is already checked — getNamespaceIndexComponent — so one condition still decides which component the sessions route renders, and the setting-off path falls back to userAgentsPlatformIndex exactly as the flag-off path does. Both off-paths matter: a self-managed instance with the instance setting off does not get the redesign, flag or no flag.

The check is user-level, not container-level. The panel's container is whatever page you happen to be on, and gating on it would make the inbox appear on one project and vanish on the next while the session list itself is the same user-scoped list either way. On .com, use User#any_group_with_ai_available? in ee/app/models/concerns/ai/user_authorizable.rb — member_namespaces.namespace_settings_with_ai_features_enabled.with_ai_supported_plan.any?, where that scope is experiment_features_enabled: true. It is cached per user, so this does not add a per-request query. Note it also carries a with_ai_supported_plan clause, which is a tighter answer than the setting alone. On self-managed, read Gitlab::CurrentSettings.instance_level_ai_beta_features_enabled?.

The value reaches the frontend the way the panel gets the rest of its server state, not through gon. DuoChatPanel::ChatComponent already builds the panel's data hash (see billing_attributes, is_saas) and has user in scope, so compute it inline there, consistent with the neighbouring attributes. Other frontends already take this setting as a data attribute — vulnerabilities_helper, ee/app/helpers/ee/groups_helper.rb and groups/security_features_helper all pass experiment_features_enabled stringified. init_duo_panel.js parseBooleans it and threads it into createRouter, which already carries per-panel config into the route table.

One implementation note: getNamespaceIndexComponent(namespace) takes only the namespace today and reaches for window.gon itself, so threading a second input changes its signature and both call sites (ai_panel_router.js and router/index.js). Reading a second gon value instead would keep the signature but leave the panel's server state split across two transports.

Acceptance criteria

  • The redesigned inbox requires both the duo_panel_session_inbox flag and the Duo experiment/beta setting.
  • The check lands in getNamespaceIndexComponent, so the sessions route still has one branch and every off-path renders userAgentsPlatformIndex.
  • .com resolves the setting per user via User#any_group_with_ai_available?; self-managed reads the instance setting.
  • A self-managed instance with the instance setting off never renders the redesign.
  • The setting reaches the frontend through the panel component's data attributes, not gon.
  • Specs cover flag on/off crossed with setting on/off, on .com and self-managed.
  • Docs and version history describe the feature as Experiment.
  • The rollout checklist in #616496 (closed) says the flag no longer decides visibility on its own.

References

Edited by 🤖 GitLab Bot 🤖