Draft: Link a project label in a group context to its project

This fixes the href that Banzai::Filter::References::LabelReferenceFilter#url_for_object builds for a project label referenced from a group context. A qualified reference such as myproject~123 resolves to a Project, but a note on a group-level work item or an epic carries a group route (group_work_items_url, group_epics_url). Those helpers accept a Project without raising and build a path under /groups/, so the label chip linked to a page that does not exist.

The filter already discarded :project_issues_url when the resolved parent turned out to be a Group. This adds the mirror case: when the parent is a Project and the url method is not a project_ route, fall back to :project_issues_url, the same default the filter picks for a Project parent and what LabelNote picks for a project resource.

Measured before and after, in a group work item note: directproj~direct-label went from /groups/directproj/-/work_items?label_name=direct-label (a 404) to /group1/directproj/-/issues?label_name=direct-label. A group label reference in the same note is unchanged.

This is a standalone bug fix, but it has to land before MR 6: the backfill writes qualified references like myproject~123 onto old rows, and without this fix a note on a group-level work item would render one of those as a chip pointing at a route that does not exist.

Part of the stack for #628135 (INC-12449), targeting 628135-04b-audit-label-event-deletion. Merge in this order. The numbers follow the branch names, so there is no MR 2: that change was folded into MR 3 and !255083 (closed) is closed.

  • MR 1 (!255082 (merged)): Render group level work item label notes against the group - merged 2026-09-21
  • MR 3 (!255091 (merged)): Store the label event reference when the event is written - merged 2026-10-07
  • MR 4 (!255092): Delete label events when their label is deleted
    • MR 4c (!257979): Validate the cascade foreign key, %19.6, branches off MR 4 and merges after MR 4 is deployed
  • MR 4b (!255238): Record deleted label events in the label audit event
  • MR 5 (!255093): Link a project label in a group context to its project <-- this MR
  • MR 6 (!255096): Backfill the reference on existing label events
  • MR 7 (!255097): Stop writing the label event reference on a read request

How to reproduce and check by hand

A label chip on a group-level work item that refers to a child project's label linked to a path under /groups/ that does not exist, so clicking it gave a 404.

In the UI:

  1. Create a group with a project in it, and create a project label.
  2. Put a label note referring to that project label on a group-level epic.
  3. Hover the label chip in the epic activity feed and read where it points.

In a rails console (gdk rails console), wrapped in a transaction that rolls back, so it leaves nothing behind:

ActiveRecord::Base.transaction do
  user   = User.find_by_username('root')
  org    = user.organizations.first
  suffix = SecureRandom.hex(4)

  group = Groups::CreateService.new(user, name: "repro-#{suffix}", path: "repro-#{suffix}",
                                    organization_id: org.id).execute[:group]
  group.add_owner(user)
  project = Projects::CreateService.new(user, name: "proj-#{suffix}", path: "proj-#{suffix}",
                                        namespace_id: group.id, organization_id: org.id).execute
  label = Labels::CreateService.new(user, title: 'direct-label').execute(project: project)
  epic  = WorkItem.create!(
    namespace: group, author: user, title: 'repro 628135',
    work_item_type: WorkItems::TypesFramework::Provider.new(group).find_by_base_type(:epic)
  )

  event = ResourceLabelEvent.create!(issue: epic, label: label, user: user, action: 'add')
  note  = event.work_item_synthetic_system_note

  puts "reference: #{event.reference.inspect}"
  puts "href:      #{note.note_html[/href="([^"]+)"/, 1].inspect}"

  raise ActiveRecord::Rollback
end

Observed output

The script above was run on the parent branch and on this branch; what follows is the actual output from both runs.

Before, on 628135-04b-audit-label-event-deletion:

reference: "proj-4a7371ae~26"
href:      "/groups/proj-4a7371ae/-/work_items?label_name=direct-label"

After, on 628135-05-label-ref-url:

reference: "proj-cd29d5e5~29"
href:      "/repro-cd29d5e5/proj-cd29d5e5/-/issues?label_name=direct-label"

Following the chip's href in a browser:

Before, on 628135-04b-audit-label-event-deletion After, on 628135-05-label-ref-url
mr05-before-chip-404 mr05-after-chip-target
404. proj-4a7371ae is a project path, and there is no group by that name. The owning project's own list, filtered by the label.

Following the before href in a browser lands on a 404 page, because the path segment is a project path and no group by that name exists. After the change, the href resolves to the owning project's own work item list, filtered by the label.

Both runs were done on a disposable GDK box whose GDK was at master 8d8af6729f, with each branch applied over it as a runtime-only patch and migrations run for real where the branch adds them.

Testing

150 examples, 0 failures, across both label_reference_filter_spec files (CE and EE) plus label_note_spec.

Related to #628135

Edited by Alexandru Croitor

Merge request reports

Loading
Loading