[FF] `duo_panel_history_stack_persistence` — AI panel history stack sessionStorage persistence
## Summary This issue is to roll out the [AI panel history stack persistence feature](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/238549) on production, which is currently behind the `duo_panel_history_stack_persistence` feature flag. When enabled, the AI panel persists its full Vue Router (abstract mode) history stack to `sessionStorage` on every navigation, and replays it on panel init. This makes the in-panel "Go back" button continue to work after a full page reload — without the flag, the panel's navigation history lives entirely in memory and is wiped on refresh. ## Owners - Most appropriate Slack channel to reach out to: `#g_agent_foundations` - Best individual to reach out to: @afontaine ## Expectations ### What are we expecting to happen? Users who open the AI panel, navigate to a session detail page, and hard-refresh the browser will see the "Go back" button stay visible and functional. Clicking it returns them to the prior in-panel route (e.g. the sessions list). No behavioural change for users who never close/refresh the panel mid-session, or who keep navigation purely within one tab without using back. ### What can go wrong and how would we detect it? - **Replay-failure stranding**: if a persisted route name no longer exists (e.g. a route was removed in a later release while a stale stack lives in someone's sessionStorage), `safeRouterPush` will throw during replay. The flag-on branch catches this, clears the corrupt stack, and pushes a mode-aware default route — so worst case is "user lands on the chat or closed route" rather than a broken panel. - **Storage growth**: stack capped at 200 entries (~16 KB) with truncate-from-oldest semantics; well below sessionStorage's 5 MB limit. - **`gon.features` exposure regression**: the flag is pushed through `ee/lib/ee/gitlab/gon_helper.rb`. If that push is dropped in a refactor, the FE check `window.gon?.features?.duoPanelHistoryStackPersistence` returns `undefined` and the feature silently disables — matches current behaviour exactly, so safe degrade. Detection: front-end error monitoring should catch any uncaught replay exceptions (none are expected since the catch is comprehensive). Manual smoke test on staging covers the happy path. ## Rollout Steps ### Rollout on non-production environments - [ ] Verify the MR is merged and deployed: `/chatops gitlab run auto_deploy status <merge-commit>` - [ ] Enable globally on non-production: `/chatops gitlab run feature set duo_panel_history_stack_persistence true --dev --pre --staging --staging-ref` - [ ] Smoke test on `staging-canary`: open AI panel → navigate Chat → Sessions → session detail → hard reload → verify Go back button works. ### Specific rollout on production - [ ] Enable for GitLab team members first: `/chatops gitlab run feature set --feature-group=gitlab_team_members duo_panel_history_stack_persistence true` - [ ] Monitor for one business day. ### Global rollout on production - [ ] 5%: `/chatops gitlab run feature set duo_panel_history_stack_persistence 5 --actors` - [ ] 25%: `/chatops gitlab run feature set duo_panel_history_stack_persistence 25 --actors` - [ ] 50%: `/chatops gitlab run feature set duo_panel_history_stack_persistence 50 --actors` - [ ] 100%: `/chatops gitlab run feature set duo_panel_history_stack_persistence 100 --actors` - [ ] Globally enable: `/chatops gitlab run feature set duo_panel_history_stack_persistence true` ### Cleanup - [ ] After one milestone at 100%, file a follow-up MR to remove the feature flag and the legacy single-route fallback in `restoreLastRoute`. The `routeState?.name === defaultRoute` short-circuit in the legacy branch is dead code once the flag is removed. - [ ] At that point, also revisit the [blocked-state hoist regression](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/238549#note_) — the hoist made the disabled-AND-previously-closed case force-open to the blocked route; consider adding `&& routeState?.name !== defaultRoute` to the gate.
issue