Add Code Owner approval removal to MR approval policies
What does this MR do and why?
Merge request approval policies could only remove all approvals on a new commit (remove_approvals_with_new_commit). This MR adds remove_code_owner_approvals_with_new_commit, removing only the approvals of Code Owners whose files changed — the granularity project-level approval settings already offer.
- Routes into the existing
ResetApprovalsService#delete_code_owner_approvals, inheriting project-levelselective_code_owner_removalssemantics. - The two are mutually exclusive; the schema rejects both, and
remove_approvals_with_new_commitwins at runtime as a guard. Removal applies only to MRs with an active non-warn-mode violation. - Precedence follows the
approval_settingsrule from epic 9696: on conflict the most secure option wins. A policy raises "Keep approvals" to selective removal, but does not narrow "Remove all approvals". - Gated behind feature flag
approval_policy_selective_code_owner_removals, disabled by default. Approval policies are already Ultimate-only, so no new license gate.
Scope: backend, YAML support, and the project settings page indicator. The setting is authored in YAML mode; the rule-mode radio group is a follow-up in !255413.
Policy schema changes
Both approval_policy_content.json and security_orchestration_policy.json gain the boolean under approval_settings, plus a not constraint rejecting both removal settings set to true.
"not": {
"required": [
"remove_approvals_with_new_commit",
"remove_code_owner_approvals_with_new_commit"
],
"properties": {
"remove_approvals_with_new_commit": { "const": true },
"remove_code_owner_approvals_with_new_commit": { "const": true }
}
}# accepted
approval_settings:
remove_code_owner_approvals_with_new_commit: true
# accepted - only one is true, so the constraint does not fire
approval_settings:
remove_approvals_with_new_commit: false
remove_code_owner_approvals_with_new_commit: true
# rejected
approval_settings:
remove_approvals_with_new_commit: true
remove_code_owner_approvals_with_new_commit: trueScreenshots or screen recordings
Local GDK, flightjs/Flight (Ultimate), flag on. CODEOWNERS maps owned/* to @code-owner-cara, master protected with Code Owner approval required, project on Keep approvals. Policy: any_merge_request / commits: any, one approval from @reviewer-remy, the new setting true. MR changes owned/payments.js, approved by both.
Recording (3x, URL overlaid) — MR goes from "Ready to merge!" to blocked when a commit touching owned/payments.js lands:
| Before: new commit | After: new commit |
|---|---|
![]() |
![]() |
- Code Owners rule
owned/*goes 1 of 1 to 0 of 1, the policy rule stays 1 of 1, widget goes "Ready to merge!" to "Merge blocked". - System note names only the Code Owner: "reset approvals from @code-owner-cara".
- A commit outside
owned/removed no approvals. - With Remove all approvals on the project (the default), all approvals go — the more secure option wins.
Project settings page — the Policy override popover appears when the policy changes behaviour, and is absent when the project is already at least as restrictive. Radios are not disabled: a policy applies per merge request, so the project setting still governs MRs outside its scope.
| Project keeps approvals | Project removes all approvals |
|---|---|
![]() |
How to set up and validate locally
- Enable the
approval_policy_selective_code_owner_removalsfeature flag. - Set the project's When a commit is added to Keep approvals — Remove all approvals is the more secure option and wins.
- In an Ultimate project with
CODEOWNERS, addremove_code_owner_approvals_with_new_commit: trueto an approval policy in YAML mode, with a rule the MR violates. - Approve as a Code Owner, then push a commit touching that owner's files — only that approval is removed. A commit touching unrelated files removes nothing.
References
- Related to &14088
- Precedence rule for
approval_settings: &9696 (closed) - Rule mode editor support: !255413
- Rollout: #604018
Notes for reviewers
- The flag resolves against the project, so enabling it for a group only reveals the editor options without enforcing in its projects.
- #597609 (closed) left
selective_code_owner_removalsatenforced_by_policy: falsebecause it had no policy counterpart. It has one now, so that table is stale — but the advisory-icon-not-lock decision in #478175 (closed) still holds. - Out of scope: a target-branch change still removes all approvals (
UpdateService#delete_approvals_on_target_branch_change), matching existing project-levelselective_code_owner_removalsbehaviour.


