Return applied state from the suggestion apply endpoints
What does this MR do and why?
PUT /suggestions/:id/apply and PUT /suggestions/batch_apply present the Suggestion records they loaded before Suggestions::ApplyService ran (suggestions.rb#L66-77). The service marks them as applied with update_all (apply_service.rb#L30-38), which leaves loaded records unchanged, so a successful apply answers "applied": false and "appliable": true, although the commit is on the source branch and a second apply is refused.
This MR loads the suggestions again after a successful apply, with one query for either endpoint, and presents those. A new request spec example for each endpoint fails on master and passes with the fix. Resetting the loaded records in place also works here, but takes a query per suggestion and keeps the inapplicable_reason each one memoizes (suggestion.rb#L56-71).
doc/api/suggestions.md showed the stale values and called the attribute applicable, while API::Entities::Suggestion exposes appliable. Both examples and both attribute tables now match the API.
Behavior change: only applied and appliable change, to what the discussions API already reports. No UI caller reads the body (the notes store resolves the discussion, and the EE AI overview conversations panel refetches), so the UI is unchanged, and I left it without a feature flag. No database changes: a successful apply runs one more SELECT on suggestions by primary key.
What I ran
On master at f568e0ae, with Ruby 3.3.11 (the second Ruby in .tool-versions):
- the request spec with the new examples and the code unchanged: 26 examples, 2 failures, the two new examples;
- the same spec with the fix: 26 examples, 0 failures, 4 pending (shared granular token examples that skip themselves here), and the same with
FOSS_ONLY=1; - two mutations of the fix, presenting the loaded record or records again: each fails its new example;
- RuboCop on the two Ruby files and
scripts/lint/commit_linter.rbon the commit: no offenses; markdownlint and Vale on the page: no errors or warnings; - on 2026-10-28, master at 3d70f0d4 had no change to the touched files or to the suggestion code they use, and the branch merges into it cleanly.
$ bin/rspec spec/requests/api/suggestions_spec.rb # master with the new examples; output abridged
1) API::Suggestions PUT /suggestions/:id/apply when successfully applies patch returns the suggestion as applied
Failure/Error: expect(json_response).to include('id' => suggestion.id, 'applied' => true, 'appliable' => false)
Diff:
-"appliable" => false,
-"applied" => true,
+"appliable" => true,
+"applied" => false,
2) API::Suggestions PUT /suggestions/batch_apply when successfully applies multiple patches as a batch returns the suggestions as applied
the missing elements were: [(include {"id" => 16, "applied" => true, "appliable" => false}), (include {"id" => 17, "applied" => true, "appliable" => false})]
26 examples, 2 failures, 4 pending
$ bin/rspec spec/requests/api/suggestions_spec.rb # this branch
26 examples, 0 failures, 4 pendingReferences
Closes #636770 (closed)
Found while working on gitlab-mcp-server.
Screenshots or screen recordings
Not applicable: the change is in an API response body, with no UI.
How to set up and validate locally
-
In GDK, add a suggestion in a merge request diff comment, and read its
idfromGET /projects/:id/merge_requests/:merge_request_iid/discussions. -
Apply it:
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" \ --url "http://gdk.test:3000/api/v4/suggestions/<suggestion_id>/apply"On master the response has
"appliable": true, "applied": false; on this branch"appliable": false, "applied": true.
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.