diff --git a/app/graphql/mutations/notes/create/note.rb b/app/graphql/mutations/notes/create/note.rb
index 5b4f01f27c1..28e012b3248 100644
--- a/app/graphql/mutations/notes/create/note.rb
+++ b/app/graphql/mutations/notes/create/note.rb
@@ -24,11 +24,10 @@ module Mutations
discussion_id = nil
if gid = args[:discussion_id]
- discussion = GitlabSchema.find_by_gid(gid)
+ discussion_id = ::GitlabSchema.parse_gid(gid, expected_type: ::Discussion).model_id
+ discussion = noteable.notes.find_discussion(discussion_id)
authorize_discussion!(discussion)
-
- discussion_id = discussion.id
end
super.merge({
diff --git a/app/services/work_items/data_sync/handlers/notes/copy_service.rb b/app/services/work_items/data_sync/handlers/notes/copy_service.rb
index 2ad19bca7f8..dbd56b2f483 100644
--- a/app/services/work_items/data_sync/handlers/notes/copy_service.rb
+++ b/app/services/work_items/data_sync/handlers/notes/copy_service.rb
@@ -17,6 +17,7 @@ module WorkItems
@target_noteable = target_noteable
@source_parent = source_noteable.resource_parent
@target_parent = target_noteable.resource_parent
+ @new_discussion_ids = {}
end
def execute
@@ -40,7 +41,8 @@ module WorkItems
private
- attr_reader :current_user, :source_noteable, :target_noteable, :source_parent, :target_parent
+ attr_reader :current_user, :source_noteable, :target_noteable, :source_parent, :target_parent,
+ :new_discussion_ids
def copy_notes_emoji(notes_ids_map)
notes_emoji = ::AwardEmoji.by_awardable('Note', notes_ids_map.keys)
@@ -79,19 +81,26 @@ module WorkItems
ids.zip(allocated_ids).to_h
end
+ # rubocop: disable Metrics/AbcSize -- Despite being long, this method is straightforward.
def new_notes(notes_batch, notes_ids_map)
notes_batch.map do |note|
+ new_discussion_ids[note.discussion_id] ||= Note.new(
+ noteable_id: target_noteable.id,
+ noteable_type: target_noteable.class.base_class
+ ).discussion_id
+
note.attributes.tap do |attrs|
attrs['id'] = notes_ids_map[note.id]
attrs['noteable_id'] = target_noteable.id
# we want this if we want to use this also to copy notes when promoting issue to epic
attrs['noteable_type'] = target_noteable.class.base_class
+ attrs['discussion_id'] = new_discussion_ids[note.discussion_id]
# need to use `try` to be able to handle Issue model and legacy Epic model instances
attrs['project_id'] = target_noteable.try(:project_id)
attrs['namespace_id'] = target_noteable.try(:namespace_id) || target_noteable.try(:group_id)
attrs['imported_from'] = 'none' # maintaining current copy notes implementation
- # this data is not changed, but it is being serialized and we need it deserialized for bulk inserts
+ # this data is not changed, but it is being serialized, and we need it deserialized for bulk inserts
attrs['position'] = note.attributes_before_type_cast['position']
attrs['original_position'] = note.attributes_before_type_cast['original_position']
attrs['change_position'] = note.attributes_before_type_cast['change_position']
@@ -104,6 +113,7 @@ module WorkItems
end
end
end
+ # rubocop: enable Metrics/AbcSize
def new_notes_emoji(notes_emoji, notes_ids_map)
notes_emoji.map do |note_emoji|
diff --git a/config/audit_events/types/api_request_access_with_scope.yml b/config/audit_events/types/api_request_access_with_scope.yml
index 015a60fd898..89ae33a93ba 100644
--- a/config/audit_events/types/api_request_access_with_scope.yml
+++ b/config/audit_events/types/api_request_access_with_scope.yml
@@ -1,6 +1,6 @@
---
name: api_request_access_with_scope
-description: A susbset of API requests authenticated by a token with an audited scope
+description: A subset of API requests authenticated by a token with an audited scope
introduced_by_issue: https://gitlab.com/gitlab-org/gitlab/-/issues/499461
introduced_by_mr: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/172548
feature_category: duo_workflow
diff --git a/doc/api/graphql/reference/_index.md b/doc/api/graphql/reference/_index.md
index 5813242c3a0..39bcb0b4a9c 100644
--- a/doc/api/graphql/reference/_index.md
+++ b/doc/api/graphql/reference/_index.md
@@ -10097,6 +10097,25 @@ Input type: `PromoteToEpicInput`
| `errors` | [`[String!]!`](#string) | Errors encountered during the mutation. |
| `issue` | [`Issue`](#issue) | Issue after mutation. |
+### `Mutation.refreshFindingTokenStatus`
+
+Input type: `RefreshFindingTokenStatusInput`
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| `vulnerabilityId` | [`VulnerabilityID!`](#vulnerabilityid) | Global ID of the Vulnerability whose token status should be refreshed. |
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| `errors` | [`[String!]!`](#string) | Errors encountered during the mutation. |
+| `findingTokenStatus` | [`VulnerabilityFindingTokenStatus`](#vulnerabilityfindingtokenstatus) | Updated token status record for the given finding. |
+
### `Mutation.refreshStandardsAdherenceChecks`
Input type: `RefreshStandardsAdherenceChecksInput`
diff --git a/doc/user/compliance/audit_event_types.md b/doc/user/compliance/audit_event_types.md
index 3c9fbdd6aef..d08326a35d9 100644
--- a/doc/user/compliance/audit_event_types.md
+++ b/doc/user/compliance/audit_event_types.md
@@ -294,11 +294,11 @@ Audit event types belong to the following product categories.
|:----------|:---------------------|:------------------|:--------------|:------|
| [`project_feature_metrics_dashboard_access_level_updated`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106919) | A project's metrics dashboard access level setting is updated | {{< icon name="check-circle" >}} Yes | GitLab [15.7](https://gitlab.com/gitlab-org/gitlab/-/issues/369289) | Project |
-### GitLab Duo Workflow
+### GitLab Duo Agent Platform
| Type name | Event triggered when | Saved to database | Introduced in | Scope |
|:----------|:---------------------|:------------------|:--------------|:------|
-| [`api_request_access_with_scope`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/172548) | A susbset of API requests authenticated by a token with an audited scope | {{< icon name="check-circle" >}} Yes | GitLab [17.7](https://gitlab.com/gitlab-org/gitlab/-/issues/499461) | User |
+| [`api_request_access_with_scope`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/172548) | A subset of API requests authenticated by a token with an audited scope | {{< icon name="check-circle" >}} Yes | GitLab [17.7](https://gitlab.com/gitlab-org/gitlab/-/issues/499461) | User |
### Dynamic application security testing
diff --git a/doc/user/group/_index.md b/doc/user/group/_index.md
index 00c37365043..40b3f07a148 100644
--- a/doc/user/group/_index.md
+++ b/doc/user/group/_index.md
@@ -99,12 +99,20 @@ the immediate parent group.
## View groups
-To explore all public groups you are a member of:
+To explore all public or internal groups:
1. On the left sidebar, select **Search or go to**.
1. Select **View all my groups**.
1. In the upper right, select **Explore groups**.
+## View groups you are a member of
+
+{{< history >}}
+
+- **Member** tab [introduced](https://gitlab.com/groups/gitlab-org/-/epics/13781) in GitLab 18.2 [with a flag](../../administration/feature_flags/_index.md) named `your_work_groups_vue`. Disabled by default.
+
+{{< /history >}}
+
To view groups where you have direct or indirect membership:
1. On the left sidebar, select **Search or go to**.
@@ -115,6 +123,8 @@ This page shows groups that you are a member of through:
- Membership of a subgroup's parent group.
- Direct or inherited membership of a project in the group or subgroup.
+If the `your_work_groups_vue` feature flag is enabled, groups that you are a member of appear in the **Member** tab.
+
## View a group
{{< history >}}
@@ -270,6 +280,12 @@ the deletion job will instead restore and unarchive the group, so the group will
### View groups pending deletion
+{{< history >}}
+
+- **Inactive** tab [introduced](https://gitlab.com/groups/gitlab-org/-/epics/13781) in GitLab 18.2 [with a flag](../../administration/feature_flags/_index.md) named `your_work_groups_vue`. Disabled by default.
+
+{{< /history >}}
+
To view a list of the subgroups that are pending deletion in a group:
1. On the left sidebar, select **Search or go to** and find your group.
@@ -277,6 +293,8 @@ To view a list of the subgroups that are pending deletion in a group:
Groups that are marked for deletion are labeled **Pending deletion**.
+If the `your_work_groups_vue` feature flag is enabled, groups marked for deletion appear in the **Inactive** tab.
+
## Delete a group immediately
{{< history >}}
diff --git a/doc/user/packages/container_registry/build_and_push_images.md b/doc/user/packages/container_registry/build_and_push_images.md
index 260db97bd8e..bc5f8524587 100644
--- a/doc/user/packages/container_registry/build_and_push_images.md
+++ b/doc/user/packages/container_registry/build_and_push_images.md
@@ -33,7 +33,13 @@ You can use Docker commands to build and push container images to your container
docker push registry.example.com/group/project/image
```
-## Configure your `.gitlab-ci.yml` file
+## Use GitLab CI/CD
+
+You can use [GitLab CI/CD](../../../ci/_index.md) to build and push container images to the
+Container Registry. You can use CI/CD to test, build, and deploy your project from the container
+image you created.
+
+### Configure your `.gitlab-ci.yml` file
You can configure your `.gitlab-ci.yml` file to build and push container images to the container registry.
@@ -50,19 +56,26 @@ You can configure your `.gitlab-ci.yml` file to build and push container images
- Don't build directly to the `latest` tag because multiple jobs may be
happening simultaneously.
-## Use GitLab CI/CD
+### Use a Docker-in-Docker container image
-You can use [GitLab CI/CD](../../../ci/_index.md) to build and push container images to the
-Container Registry. You can use CI/CD to test, build, and deploy your project from the container
-image you created.
+You can use your own Docker-in-Docker (DinD)
+container images with the container registry or Dependency Proxy.
-### Use a Docker-in-Docker container image from your container registry
+Use DinD to build, test, and deploy containerized
+applications from your CI/CD pipeline.
-You can use your own container images for Docker-in-Docker.
+Prerequisites:
-1. Set up [Docker-in-Docker](../../../ci/docker/using_docker_build.md#use-docker-in-docker).
-1. Update the `image` and `service` to point to your registry.
-1. Add a service [alias](../../../ci/services/_index.md#available-settings-for-services).
+- Set up [Docker-in-Docker](../../../ci/docker/using_docker_build.md#use-docker-in-docker).
+
+{{< tabs >}}
+
+{{< tab title="From the container registry" >}}
+
+In your `.gitlab-ci.yml` file:
+
+- Update `image` and `services` to point to your registry.
+- Add a service [alias](../../../ci/services/_index.md#available-settings-for-services).
Your `.gitlab-ci.yml` should look similar to this:
@@ -78,20 +91,14 @@ build:
- docker run my-docker-image /script/to/run/tests
```
-If you forget to set the service alias, the container image can't find the `dind` service,
-and an error like the following is shown:
+{{< /tab >}}
-```plaintext
-error during connect: Get http://docker:2376/v1.39/info: dial tcp: lookup docker on 192.168.0.1:53: no such host
-```
+{{< tab title="With the Dependency Proxy" >}}
-### Use a Docker-in-Docker container image with Dependency Proxy
+In your `.gitlab-ci.yml` file:
-You can use your own container images with Dependency Proxy.
-
-1. Set up [Docker-in-Docker](../../../ci/docker/using_docker_build.md#use-docker-in-docker).
-1. Update the `image` and `service` to point to your registry.
-1. Add a service [alias](../../../ci/services/_index.md#available-settings-for-services).
+- Update `image` and `services` to point to your dependency proxy.
+- Add a service [alias](../../../ci/services/_index.md#available-settings-for-services).
Your `.gitlab-ci.yml` should look similar to this:
@@ -107,6 +114,10 @@ build:
- docker run my-docker-image /script/to/run/tests
```
+{{< /tab >}}
+
+{{< /tabs >}}
+
If you forget to set the service alias, the container image can't find the `dind` service,
and an error like the following is shown:
@@ -116,7 +127,7 @@ error during connect: Get http://docker:2376/v1.39/info: dial tcp: lookup docker
## Container registry examples with GitLab CI/CD
-If you're using Docker-in-Docker on your runners, your `.gitlab-ci.yml` file should look similar to this:
+If you're using DinD on your runners, your `.gitlab-ci.yml` file should look similar to this:
```yaml
build:
diff --git a/scripts/frontend/quarantined_vue3_specs.txt b/scripts/frontend/quarantined_vue3_specs.txt
index 2702700b436..db4ae910bb0 100644
--- a/scripts/frontend/quarantined_vue3_specs.txt
+++ b/scripts/frontend/quarantined_vue3_specs.txt
@@ -86,7 +86,6 @@ spec/frontend/jira_import/components/jira_import_form_spec.js
spec/frontend/lib/utils/breadcrumbs_spec.js
spec/frontend/lib/utils/confirm_via_gl_modal/confirm_action_spec.js
spec/frontend/members/components/app_spec.js
-spec/frontend/members/components/modals/leave_modal_spec.js
spec/frontend/members/components/table/max_role_spec.js
spec/frontend/members/components/table/members_table_spec.js
spec/frontend/ml/model_registry/components/model_edit_spec.js
@@ -110,7 +109,6 @@ spec/frontend/super_sidebar/components/user_menu_spec.js
spec/frontend/tooltips/index_spec.js
spec/frontend/vue_alerts_spec.js
spec/frontend/vue_popovers_spec.js
-spec/frontend/vue_shared/components/design_management/design_note_pin_spec.js
spec/frontend/vue_shared/components/file_tree_spec.js
spec/frontend/vue_shared/components/filtered_search_bar/tokens/date_token_spec.js
spec/frontend/vue_shared/components/metric_images/metric_image_details_modal_spec.js
diff --git a/spec/frontend/members/components/modals/leave_modal_spec.js b/spec/frontend/members/components/modals/leave_modal_spec.js
index 3b2b8cee08f..ebb5057ed02 100644
--- a/spec/frontend/members/components/modals/leave_modal_spec.js
+++ b/spec/frontend/members/components/modals/leave_modal_spec.js
@@ -180,7 +180,7 @@ describe('LeaveModal', () => {
const submitSpy = jest.spyOn(findForm().element, 'submit');
- findModal().findByText('Leave').trigger('click');
+ findModal().vm.$emit('primary');
expect(submitSpy).toHaveBeenCalled();
diff --git a/spec/frontend/vue_shared/components/design_management/design_note_pin_spec.js b/spec/frontend/vue_shared/components/design_management/design_note_pin_spec.js
index ca9c2b7d381..156846e1873 100644
--- a/spec/frontend/vue_shared/components/design_management/design_note_pin_spec.js
+++ b/spec/frontend/vue_shared/components/design_management/design_note_pin_spec.js
@@ -1,4 +1,5 @@
import { shallowMount } from '@vue/test-utils';
+import { assertProps } from 'helpers/assert_props';
import DesignNotePin from '~/vue_shared/components/design_management/design_note_pin.vue';
describe('Design note pin component', () => {
@@ -61,12 +62,9 @@ describe('Design note pin component', () => {
});
it('throws when passed any other value except `sm` or `md`', () => {
- jest.spyOn(console, 'error').mockImplementation(() => {});
-
- createComponent({ size: 'lg' });
-
- // eslint-disable-next-line no-console
- expect(console.error).toHaveBeenCalled();
+ expect(() => {
+ assertProps(DesignNotePin, { size: 'lg' });
+ }).toThrow('Invalid prop: custom validator check failed');
});
});
diff --git a/spec/requests/api/graphql/mutations/notes/create/note_spec.rb b/spec/requests/api/graphql/mutations/notes/create/note_spec.rb
index 13d9f823636..e8c4f6a6453 100644
--- a/spec/requests/api/graphql/mutations/notes/create/note_spec.rb
+++ b/spec/requests/api/graphql/mutations/notes/create/note_spec.rb
@@ -61,10 +61,23 @@ RSpec.describe 'Adding a Note', feature_category: :team_planning do
context 'when the user has permission to create notes on the discussion' do
let(:discussion) { create(:discussion_note, project: project).to_discussion }
- it 'creates a Note in a discussion' do
- post_graphql_mutation(mutation, current_user: current_user)
+ context 'when discussion is not on the noteable' do
+ it 'checks noteable and discussion noteable' do
+ expect(noteable.id).not_to eq(discussion.noteable_id)
+ end
- expect(mutation_response['note']['discussion']).to match a_graphql_entity_for(discussion)
+ it_behaves_like 'a mutation that returns top-level errors',
+ errors: ["The discussion does not exist or you don't have permission to perform this action"]
+ end
+
+ context 'when the discussion is on the noteable' do
+ let(:noteable) { discussion.noteable }
+
+ it 'creates a Note in a discussion' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(mutation_response['note']['discussion']).to match a_graphql_entity_for(discussion)
+ end
end
context 'when the discussion_id is not for a Discussion' do
diff --git a/spec/services/work_items/data_sync/handlers/notes/copy_service_spec.rb b/spec/services/work_items/data_sync/handlers/notes/copy_service_spec.rb
index c7f4291bdca..7846cb547d7 100644
--- a/spec/services/work_items/data_sync/handlers/notes/copy_service_spec.rb
+++ b/spec/services/work_items/data_sync/handlers/notes/copy_service_spec.rb
@@ -63,7 +63,7 @@ RSpec.describe WorkItems::DataSync::Handlers::Notes::CopyService, feature_catego
# 4 notes are copied to the target work item: 2 system notes and 2 user notes
# 2 system notes had also description version metadata
- # 2 user notes notes had also description version metadata
+ # 2 user notes had also description version metadata
expect { execute_service }.to change { ::Note.count }.by(5).and(
change { ::SystemNoteMetadata.count }.by(2)).and(
change { ::DescriptionVersion.count }.by(1)).and(
@@ -71,7 +71,10 @@ RSpec.describe WorkItems::DataSync::Handlers::Notes::CopyService, feature_catego
change { ::IssueUserMention.count }.by(2))
notes_details = target_work_item.reload.notes.pluck(:note, :discussion_id)
- expect(notes_details).to match_array(expected_notes_details)
+ # same number of discussions in target and source work items
+ expect(notes_details.size).to eq(expected_notes_details.size)
+ # but with different discussion ids
+ expect(notes_details).not_to match_array(expected_notes_details)
end
it 'sets correct attributes from target', :aggregate_failures do
@@ -82,5 +85,100 @@ RSpec.describe WorkItems::DataSync::Handlers::Notes::CopyService, feature_catego
expect(expected_description_version.namespace_id).to eq(target_work_item.namespace_id)
expect(expected_description_version.issue_id).to eq(target_work_item.id)
end
+
+ describe 'discussion_id generation' do
+ it 'generates new discussion_ids for copied notes' do
+ original_discussion_ids = work_item.notes.pluck(:discussion_id).uniq
+
+ execute_service
+
+ copied_discussion_ids = target_work_item.reload.notes.pluck(:discussion_id).uniq
+
+ # All copied notes should have different discussion_ids from originals
+ expect(copied_discussion_ids).not_to include(*original_discussion_ids)
+ # Each copied discussion_id should be a valid 40-character hex string
+ copied_discussion_ids.all? { |discussion_id| expect(discussion_id).to match(/\A\h{40}\z/) }
+ end
+
+ it 'reuses the same new discussion_id for notes in the same discussion' do
+ # Create notes that are part of the same discussion
+ discussion_note = create(:note, project: work_item.project, noteable: work_item)
+ reply_note = create(:note,
+ project: work_item.project,
+ noteable: work_item,
+ discussion_id: discussion_note.discussion_id,
+ in_reply_to: discussion_note
+ )
+
+ execute_service
+
+ copied_notes = target_work_item.reload.notes.where(note: [discussion_note.note, reply_note.note])
+ copied_discussion_ids = copied_notes.pluck(:discussion_id).uniq
+
+ # Both copied notes should have the same new discussion_id
+ expect(copied_discussion_ids.size).to eq(1)
+ # But it should be different from the original
+ expect(copied_discussion_ids.first).not_to eq(discussion_note.discussion_id)
+ end
+
+ it 'generates different discussion_ids for different original discussions' do
+ # Create two separate discussions
+ discussion1_note = create(:note, project: work_item.project, noteable: work_item)
+ discussion2_note = create(:note, project: work_item.project, noteable: work_item)
+
+ execute_service
+
+ copied_notes = target_work_item.reload.notes.where(note: [discussion1_note.note, discussion2_note.note])
+ copied_discussion_ids = copied_notes.pluck(:discussion_id)
+
+ # Each copied note should have a different discussion_id
+ expect(copied_discussion_ids.uniq.size).to eq(2)
+ expect(copied_discussion_ids).not_to include(discussion1_note.discussion_id, discussion2_note.discussion_id)
+ end
+
+ it 'calls Discussion.discussion_id to generate new discussion_ids' do
+ expect(::Discussion).to receive(:discussion_id).at_least(:once).and_call_original
+
+ execute_service
+ end
+
+ it 'maintains discussion structure when copying notes with replies' do
+ # Create a discussion with multiple replies
+ parent_note = create(:note, project: work_item.project, noteable: work_item, note: 'Parent note')
+ create(:note,
+ project: work_item.project,
+ noteable: work_item,
+ note: 'Reply 1',
+ discussion_id: parent_note.discussion_id,
+ in_reply_to: parent_note
+ )
+ create(:note,
+ project: work_item.project,
+ noteable: work_item,
+ note: 'Reply 2',
+ discussion_id: parent_note.discussion_id,
+ in_reply_to: parent_note
+ )
+
+ execute_service
+
+ # Find the copied notes
+ copied_parent = target_work_item.reload.notes.find_by(note: 'Parent note')
+ copied_reply1 = target_work_item.notes.find_by(note: 'Reply 1')
+ copied_reply2 = target_work_item.notes.find_by(note: 'Reply 2')
+
+ # All copied notes should have the same new discussion_id
+ expect(copied_parent.discussion_id).to eq(copied_reply1.discussion_id)
+ expect(copied_parent.discussion_id).to eq(copied_reply2.discussion_id)
+
+ # But different from the original
+ expect(copied_parent.discussion_id).not_to eq(parent_note.discussion_id)
+
+ # Verify the discussion structure is maintained
+ discussion = target_work_item.notes.find_discussion(copied_parent.discussion_id)
+ expect(discussion.notes.size).to eq(3)
+ expect(discussion.notes.map(&:note)).to contain_exactly('Parent note', 'Reply 1', 'Reply 2')
+ end
+ end
end
end
diff --git a/spec/services/work_items/data_sync/widgets/notes_spec.rb b/spec/services/work_items/data_sync/widgets/notes_spec.rb
index a3b67d46319..6f3c9957c22 100644
--- a/spec/services/work_items/data_sync/widgets/notes_spec.rb
+++ b/spec/services/work_items/data_sync/widgets/notes_spec.rb
@@ -105,7 +105,7 @@ RSpec.describe WorkItems::DataSync::Widgets::Notes, feature_category: :team_plan
# 4 notes are copied to the target work item: 2 system notes and 2 user notes
# 2 system notes had also description version metadata
- # 2 user notes notes had also description version metadata
+ # 2 user notes had also description version metadata
expect { callback.after_create }.to change { ::Note.count }.by(5).and(
change { ::SystemNoteMetadata.count }.by(2)).and(
change { ::DescriptionVersion.count }.by(1)).and(
@@ -116,7 +116,11 @@ RSpec.describe WorkItems::DataSync::Widgets::Notes, feature_category: :team_plan
change { ::IssueUserMention.count }.by(2))
notes_details = target_work_item.reload.notes.pluck(:note, :discussion_id)
- expect(notes_details).to match_array(expected_notes_details)
+ # size and notes would match
+ expect(notes_details.size).to eq(expected_notes_details.size)
+ expect(notes_details.map(&:first)).to match_array(expected_notes_details.map(&:first))
+ # but discussion_ids would not
+ expect(notes_details.map(&:last)).not_to match_array(expected_notes_details.map(&:last))
end
end
diff --git a/tooling/audit_events/docs/templates/audit_event_types.md.erb b/tooling/audit_events/docs/templates/audit_event_types.md.erb
index ca4a0fa7709..61f3aac5f6f 100644
--- a/tooling/audit_events/docs/templates/audit_event_types.md.erb
+++ b/tooling/audit_events/docs/templates/audit_event_types.md.erb
@@ -9,7 +9,7 @@
<% def humanize(feature_category) %>
<% case feature_category %>
<% when 'duo_workflow' %>
-<% "GitLab Duo Workflow" %>
+<% "GitLab Duo Agent Platform" %>
<% when 'mlops' %>
<% "MLOps" %>
<% when 'not_owned' %>