Add audit event for CI runner tag changes
What does this MR do and why?
Adds a ci_runner_tags_updated audit event, emitted when a runner's tags (tag_list) change through Ci::Runners::UpdateRunnerService.
Runner lifecycle events (register, unregister, create, assign, unassign, bulk-delete) are already audited, but editing a runner was not. Tags are the runner attribute most relevant to CI access control, so a tag that silently appears or changes on a runner left customers with no audit trail.
This matters most for GitLab Duo. Duo Agent Platform flows only run on runners tagged gitlab--duo (Configure runners to execute flows), so that tag decides where Duo flows execute and which runners receive their credentials. Organizations that need a strict audit trail of where AI tools run can now see who added or removed the tag, and when.
Resolves #637852
Changes
ee/app/services/ee/ci/runners/update_runner_service.rb(new): EE override of#execute. When the update includestag_list, it snapshots the tags before the update and, on success, emits the audit event if the sorted tag set changed. The event stores the old and new tag arrays inadditional_details. Updates withouttag_listgo straight to the CE service.app/services/ci/runners/update_runner_service.rb: adds the trailingprepend_mod. The CE service had no EE extension before.ee/app/services/audit_events/runner_audit_event_service.rb: accepts an optionaladditional_details:keyword that is stored with the event. Existing callers don't pass it and are unchanged.ee/config/audit_events/types/ci_runner_tags_updated.yml(new): event type definition (saved to database, streamed, Instance/Group/Project scope).ee/spec/services/ee/ci/runners/update_runner_service_spec.rb(new): covers a tag change, all tags removed, tags added to an untagged runner, a tag containing a comma, a tag namednone, instance and project runners, unchanged tags, a non-tag attribute change, and a failed update.ee/spec/services/audit_events/runner_audit_event_service_spec.rb: covers passingadditional_detailsthrough to the auditor.doc/user/compliance/audit_event_types.md: regenerated from the YAML.
Implementation notes
- Tags come from
acts-as-taggable, sorunner.previous_changesdoes not track them. The change is detected by comparing sortedtag_listbefore and after, similar to howee/app/services/ci/runner_controllers/update_service.rbsnapshotsold_state. - Reuses
AuditEvents::RunnerAuditEventService, which resolves the scope (instance for instance runners, otherwiserunner.owner), author, target, and message formatting. Old and new tags go into the%{from}/%{to}message placeholders as bracketed lists, for example "Changed instance CI runner tags from [shared] to [docker, shared]". An empty list is shown as[], so it reads differently from a tag namednone([none]). - The message is a readable summary, and the UI and CSV export show only that summary. Because a tag can contain a comma, one tag
"docker, gitlab--duo"and two tagsdockerandgitlab--duoboth read as[docker, gitlab--duo]. The exact tag arrays are stored in the event details aschanges: [{ change: :tag_list, from:, to: }]. - The change is nested under
changes:, as inGroups::GroupLinks::UpdateService. A top-levelchangekey would makeAudit::Detailsbuild the displayed action from the raw arrays, so the UI and CSV export would showChanged tag list from ["docker"] to [...]instead of the message. A spec checks the displayed action. - No explicit license check:
Gitlab::Audit::Auditordecides what is persisted or streamed, consistent with the sibling runner audit services.
Out of scope
- Auditing other mutable runner settings (
locked,access_level,run_untagged,maximum_timeout,paused,description). The same pattern applies.
Screenshots or screen recordings
How to set up and validate locally
- In the GDK, edit a group runner's tags (Group > Build > Runners > Edit).
- Go to Group > Secure > Audit events and confirm a "Changed group CI runner tags from ... to ..." entry.
- Save the runner again without changing tags and confirm no new entry.
bin/rspec ee/spec/services/ee/ci/runners/update_runner_service_spec.rb ee/spec/services/audit_events/runner_audit_event_service_spec.rbMR acceptance checklist
Evaluate this MR against the MR acceptance checklist.

