API: ai_workflows scope refuses the blobs/batch read, breaking no-clone repository access

Everyone can contribute. Help move this issue forward while earning points, leveling up and collecting rewards.

Problem

API::Repositories admits the ai_workflows scope on GET and HEAD only:

# lib/api/repositories.rb:19
allow_access_with_scope :ai_workflows, if: ->(request) { request.get? || request.head? }

POST :id/repository/blobs/batch is a read — the file list travels in the request body because it does not fit in a query string — so a Duo workflow token is refused with insufficient_scope on content the same scope can already read one file at a time through GET files/:path/raw.

Evidence

Found while running the Duo developer flow with no working tree, reaching the repository through the ws CLI instead of a clone. ws performed the whole task — grep, cat x12, replace x5, diff — then both ws materialize and ws checkpoint failed here:

POST .../repository/blobs/batch: 403 {error: insufficient_scope},
{error_description: The request requires higher privileges than provided by the
access token.}, {scope: ai_workflows api read_api}

The agent then fell back to git clone, reapplied the same change by hand, and opened the merge request that way. The run scored mr_created = 1 while the no-clone write path was entirely broken — a false positive that is invisible unless you read the tool calls.

Proposed fix

Admit the batch endpoint by path rather than widening the gate to POST in general, so no write route is affected:

allow_access_with_scope :ai_workflows, if: ->(request) do
  request.get? || request.head? ||
    (request.post? && request.path.end_with?('/repository/blobs/batch'))
end

Verified: with this change ws checkpoint succeeded and returned a real commit (20a3c40d, 3 files, +3/-3) with no clone anywhere in the run.

Context

Surfaced while measuring no-clone repository access for gitlab-org#23427

Edited by 🤖 GitLab Bot 🤖