Rename get_previous_session_context tool to get_session_context in AI Catalog
What does this MR do and why?
Renames the AI Catalog built-in tool get_previous_session_context to get_session_context to match the rename already shipped in the AI Gateway (gitlab-org/modelops/applied-ml/code-suggestions/ai-assist!6283 (merged)). The numeric tool id (58) is unchanged, to preserve tool selections persisted in Ai::Catalog::ItemVersion#def_tools.
Updates the tool's metadata in ee/lib/ai/catalog/built_in_tool_definitions.rb and its key in the privilege group mapping in ee/lib/ai/tool_rules/registry.rb — this one matters functionally, since the old key would have dropped the tool out of all_tool_names, default_permission_of, category_for, and action_type_of. Also updates a stale comment in ee/app/graphql/ee/types/query_type.rb and the test strings in ee/spec/requests/api/graphql/ai/catalog/built_in_tools_spec.rb.
Also adds a post-deploy data migration for existing ai_tool_rules rows left on the old tool name. Without it, an admin's Deny or Ask on this tool becomes invisible in the governance UI and silently falls back to auto-allow, since read_only_gitlab is in DEFAULT_PREAPPROVED_GROUPS. The migration renames rows with no conflicting new-named counterpart for the same namespace/project.
A collision with idx_ai_tool_rules_ns_proj_tool_unique happens when an admin already set a rule under the new name, either before this migration reached that row or concurrently while it was running (the second case is caught by rescuing the resulting unique-index violation). That row's explicitly-set columns were chosen under the tool's current name, so the migration keeps them as-is. Any column it left unset gets backfilled from the old row instead of silently defaulting, since the rename bug hid that surface's history from the admin when they made the edit. The old row is deleted once the merge is done.
Queries used by this migration (required for database review)
Local GDK data. No Database Lab access from here, which the docs treat as an acceptable fallback ("seed a development environment... provide output from EXPLAIN ANALYZE").
1. Batch scan — find the next batch of stale rows (each_batch, two queries: bound probe + fetch)
SELECT "ai_tool_rules"."id" FROM "ai_tool_rules"
WHERE "ai_tool_rules"."tool_name" = 'get_previous_session_context'
ORDER BY "ai_tool_rules"."id" ASC LIMIT 1;Limit (cost=7.78..7.78 rows=1 width=8) (actual time=0.018..0.018 rows=1 loops=1)
-> Sort (cost=7.78..7.78 rows=2 width=8) (actual time=0.018..0.018 rows=1 loops=1)
Sort Key: id
-> Index Scan using idx_ai_tool_rules_ns_proj_tool_unique on ai_tool_rules
Index Cond: (tool_name = 'get_previous_session_context'::text)
Planning Time: 0.384 ms
Execution Time: 0.032 msSELECT "ai_tool_rules".* FROM "ai_tool_rules"
WHERE "ai_tool_rules"."tool_name" = 'get_previous_session_context' AND "ai_tool_rules"."id" >= 22;Index Scan using idx_ai_tool_rules_ns_proj_tool_unique on ai_tool_rules (cost=0.15..7.77 rows=1 width=142)
Index Cond: (tool_name = 'get_previous_session_context'::text)
Filter: (id >= 22)
Planning Time: 0.018 ms
Execution Time: 0.004 ms2. find_fresh_rule — two variants depending on whether project_id is set
SELECT "ai_tool_rules".* FROM "ai_tool_rules"
WHERE "ai_tool_rules"."namespace_id" = 24 AND "ai_tool_rules"."project_id" IS NULL
AND "ai_tool_rules"."tool_name" = 'get_session_context' LIMIT 1;Limit (cost=0.15..2.17 rows=1 width=142) (actual time=0.003..0.003 rows=1 loops=1)
-> Index Scan using idx_ai_tool_rules_ns_proj_tool_unique on ai_tool_rules
Index Cond: ((namespace_id = 24) AND (project_id IS NULL) AND (tool_name = 'get_session_context'::text))
Planning Time: 0.016 ms
Execution Time: 0.006 msSELECT "ai_tool_rules".* FROM "ai_tool_rules"
WHERE "ai_tool_rules"."namespace_id" = 24 AND "ai_tool_rules"."project_id" = 278964
AND "ai_tool_rules"."tool_name" = 'get_session_context' LIMIT 1;Limit (cost=0.15..2.17 rows=1 width=142) (actual time=0.001..0.001 rows=0 loops=1)
-> Index Scan using idx_ai_tool_rules_ns_proj_tool_unique on ai_tool_rules
Index Cond: ((namespace_id = 24) AND (project_id = 278964) AND (tool_name = 'get_session_context'::text))
Planning Time: 0.013 ms
Execution Time: 0.004 ms3. merge_stale_into_fresh — backfill the fresh row (update_columns)
UPDATE "ai_tool_rules" SET "web_access" = 2, "local_access" = 2, "background_access" = 2
WHERE "ai_tool_rules"."id" = 26;Update on ai_tool_rules (cost=0.15..2.17 rows=0 width=0)
-> Index Scan using ai_tool_rules_pkey on ai_tool_rules
Index Cond: (id = 26)(UPDATE always reports rows=0 at the top level. The Index Scan line below is what shows the row actually being touched.)
4. merge_stale_into_fresh — delete the stale row
DELETE FROM "ai_tool_rules" WHERE "ai_tool_rules"."id" = 26;Delete on ai_tool_rules (cost=0.15..2.17 rows=0 width=0)
-> Index Scan using ai_tool_rules_pkey on ai_tool_rules
Index Cond: (id = 26)5. Plain rename path (update_column, no conflict)
UPDATE "ai_tool_rules" SET "tool_name" = 'get_session_context' WHERE "ai_tool_rules"."id" = 26;Update on ai_tool_rules (cost=0.15..2.17 rows=0 width=0)
-> Index Scan using ai_tool_rules_pkey on ai_tool_rules
Index Cond: (id = 26)All six hit an index, either the unique index or the primary key. No sequential scans.
References
- !245344 (merged)
- gitlab-org/modelops/applied-ml/code-suggestions/ai-assist!6283 (merged)
- https://gitlab.com/gitlab-org/gitlab/-/issues/628007
Screenshots or screen recordings
| Before | After |
|---|---|
![]() |
![]() |
How to set up and validate locally
- On
master, open the governance settings page for a Duo-enabled group (e.g.gitlab-org,/groups/gitlab-org/-/settings/gitlab_duo/governance) and set "Get Previous Session Context" to Deny. - Check out this branch and restart
rails-web(the tool catalog is memoized per process, so a page reload alone won't pick up the rename). - Reload the governance page. The tool now shows as "Get Session Context" with no rule set, defaulting to Allow. This is the bug: the Deny from step 1 is now invisible.
- Run
bin/rails db:migrate. - Reload again: "Get Session Context" now correctly shows Deny.
Confirm at the DB level:
SELECT id, namespace_id, project_id, tool_name, web_access, local_access, background_access
FROM ai_tool_rules
WHERE namespace_id = (SELECT id FROM namespaces WHERE path = 'gitlab-org' AND type = 'Group')
ORDER BY id;tool_name flips from get_previous_session_context to get_session_context after the migration runs.
Confirmed enforcement, not just the UI label, via Ai::ToolRules::ResolutionService.new(namespace: group, project: nil, surface: :chat).execute in the Rails console. get_session_context shows up in denied_tools after the migration.
The merge-on-conflict path is covered by the migration spec rather than manual steps, since reproducing it by hand means racing an admin edit against the exact code-deploy-to-migration window:
bundle exec rspec spec/migrations/20260909092314_rename_get_previous_session_context_tool_rules_spec.rbMR 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.

