Add ai_workflows scope to work item write REST endpoints
What does this MR do and why?
Opts the work-item write REST endpoints into the ai_workflows OAuth scope so that agent sessions running with the composite ai_workflows token can complete common work-item write flows.
Today an agent can read a work item (WorkItems::Show already allows ai_workflows) but gets 403 insufficient_scope when it tries to create, update, delete, or comment on one. This was hit in practice: a session asked to "make an epic" couldn't, and the human had to promote the issue manually (see References).
The same write capability is already exposed to the broader api scope, and the equivalent GraphQL mutations (WorkItems::Create / WorkItems::Update) already allow ai_workflows. This change closes the REST gap without granting any capability the agent doesn't already have through another surface.
Changes
Add include ::API::Concerns::AiWorkflowsAccess + allow_ai_workflows_access (matching the existing pattern in WorkItems::Show, API::Issues, API::Epics) to:
lib/api/work_items/create.rb(POST)lib/api/work_items/update.rb(PATCH)lib/api/work_items/notes.rb(POST create note)
lib/api/work_items/delete.rb is deliberately left out: DELETE stays 403 insufficient_scope for ai_workflows tokens. GraphQL's workItemDelete mutation only allows the api scope, and delete_spec.rb pins that REST stays consistent with it.
lib/api/concerns/ai_workflows_access.rb now also allows PATCH (previously GET/HEAD/POST/PUT only). That change applies to every class that includes the concern; none of the others (WorkItems::Show, API::Issues, API::Epics, API::Commits, API::Glql, the Orbit/internal endpoints) has a PATCH route today, so it's additive only for work items.
creation.rb and update.rb now build a Gitlab::Auth::ScopeValidator from the current request and pass it into WorkItems::CreateService / WorkItems::UpdateService as scope_validator:. A quick action outside ALLOWED_AI_QUICK_ACTIONS (e.g. /close) in a description returns 403 for ai_workflows tokens instead of executing silently, matching how create_note_for already behaves in notes.rb.
Notes:
lib/api/work_items/discussions.rbandlib/api/work_items/linked_items.rbare not reachable under theai_workflowsscope.allow_access_with_scopegrants are per endpoint class, so including the concern oncreate.rb/update.rb/notes.rbdoesn't extend to sibling classes. Bringing those two in is out of scope here; tracked in https://gitlab.com/gitlab-org/gitlab/-/work_items/630767.- Adds request specs exercising each write endpoint with an
oauth_access_tokenscoped to[:ai_workflows], plusspec/lib/api/concerns/ai_workflows_access_spec.rbpinning the verb matrix directly on the concern (read and write verbs allowed,DELETErejected).
References
- https://gitlab.com/gitlab-org/gitlab/-/work_items/630767
- #629929 (closed)
- Investigation: #629929 (comment 3877389672)
- Originating agent session: https://gitlab.com/gitlab-org/gitlab/-/automate/agent-sessions/8352154
Screenshots or screen recordings
N/A — backend REST authorization change, no UI.
How to set up and validate locally
- Enable the feature flag (the work-items REST API is behind
work_item_rest_api):Feature.enable(:work_item_rest_api) - Create an OAuth access token for a user with the
ai_workflowsscope. - Create a work item via REST using that token:
curl --request POST \ --header "Authorization: Bearer <ai_workflows_token>" \ --url "https://gitlab.example.com/api/v4/projects/<project_id>/-/work_items" \ --header "Content-Type: application/json" \ --data '{ "title": "Agent task", "work_item_type_name": "task" }' - Confirm the request returns
201 Created(previously403 insufficient_scope). - Update the same work item with the same token:
Confirm the request returns
curl --request PATCH \ --header "Authorization: Bearer <ai_workflows_token>" \ --url "https://gitlab.example.com/api/v4/projects/<project_id>/-/work_items/<iid>" \ --header "Content-Type: application/json" \ --data '{ "title": "Agent task (updated)" }'200 OK(previously403 insufficient_scope, since the concern didn't allow PATCH before this MR). - Retry the create with a quick action in the description instead of a plain title:
Confirm the request returns
curl --request POST \ --header "Authorization: Bearer <ai_workflows_token>" \ --url "https://gitlab.example.com/api/v4/projects/<project_id>/-/work_items" \ --header "Content-Type: application/json" \ --data '{ "title": "Agent task", "work_item_type_name": "task", "description": "/close" }'403with the quick-actions-not-allowed message, not a work item that got silently closed. - See
spec/lib/api/concerns/ai_workflows_access_spec.rbfor the verb matrix pinned directly on the concern.
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.