Skip to content

[COMMS-1067] Journal admin deletion of a label - #26191

Draft
akabiru wants to merge 12 commits into
devfrom
implementation/comms-1067-journal-admin-label-deletion
Draft

akabiru wants to merge 12 commits into
devfrom
implementation/comms-1067-journal-admin-label-deletion

Conversation

@akabiru

@akabiru akabiru commented Oct 21, 2026 •

Copy link
Copy Markdown
Member

Ticket

https://community.openproject.org/wp/COMMS-1067

What are you trying to accomplish?

Deleting a label in Administration > Labels removed it from every work package without a trace. Each affected work package now gets an activity entry authored by the deleting admin, "Label removed: 'Frontend' was deleted", or "Label removed: 'Frontend', 'Backend' were deleted" when the same admin deletes several labels within the journal aggregation window. The entry carries no "Labels changed" line of its own; the removed labels are named in the cause.

Screenshots

Activity entry on an affected work package

What approach did you choose and why?

sequenceDiagram
  participant A as Admin in the labels administration
  participant C as Admin::LabelsController (destroy)
  participant S as Labels::DeleteService
  participant J as Labels::JournalizeDeletionJob
  participant CS as Journals::CreateService
  participant T as Activity tab

  A->>C: DELETE /admin/labels/:id
  C->>S: call
  S->>S: pluck labeled work package ids
  S->>S: destroy label (labelings cascade)
  S->>J: perform_later(work_package_ids, label_id, label_name, user_id)
  S-->>C: success
  C-->>A: redirect, flash "Successful deletion."
  J->>J: skip work packages whose journals already carry this label id
  loop each remaining work package, under its mutex
    J->>CS: call(cause: CausedByLabelDeletion(label_ids, label_names))
    alt same admin deleted another label within the aggregation window
      CS->>CS: aggregate into the previous entry, merge the label lists
    else
      CS->>CS: new journal with the cause and the current label snapshot
    end
  end
  A->>T: open an affected work package
  T->>T: render "Label removed: 'A', 'B' were deleted"
  Note over T: the "Labels changed" detail is hidden on this entry
Loading

The deletion itself stays synchronous and cheap; the journals are written afterwards by a job, since a label can sit on thousands of work packages. The cause carries the label ids and names because the rows are gone by then (the sprint migration cause snapshots a version name the same way). Writing through Journals::CreateService directly means no notifications go out for these entries, and the job is idempotent: one indexed lookup finds the work packages whose cause already contains the label id.

Merging consecutive deletions needed a small generic hook: a cause may declare that it extends the predecessor's cause (aggregatable_with? / aggregate_with, no-op defaults on CauseOfChange::Base), and the journal service then stores the merged cause on the aggregated journal. The labels detail is hidden only in the activities tab; the API still returns it. A manual label edit by the same admin inside the same window merges into the entry and is hidden with it, which is acceptable for an admin action.

AI involvement

Collaborative – AI generated a substantial part of the code; I reviewed and understand every line.

Merge checklist

  • Added/updated tests
  • Added/updated documentation in Lookbook (patterns, previews, etc)
  • Tested major browsers (Chrome, Firefox, Edge, ...)

@akabiru akabiru self-assigned this Oct 21, 2026
@akabiru
akabiru added this pull request to stack #26192 October 21, 2026 08:19
@github-actions github-actions Bot added ai: Directed 🪄 A human specified the requirements and AI implemented most of it; They validated via testing. ai: Collaborative 💻 AI generated a substantial part of the code; A human reviewed and understands every line. and removed ai: Directed 🪄 A human specified the requirements and AI implemented most of it; They validated via testing. labels Oct 21, 2026
@akabiru
akabiru force-pushed the implementation/comms-1067-journal-admin-label-deletion branch 3 times, most recently from eba7da9 to 0a2ff71 Compare October 21, 2026 12:39
@github-actions

Copy link
Copy Markdown

Warning

Flaky specs

  • rspec ./modules/bim/spec/features/viewer/create_viewpoint_spec.rb[1:2:1:1]
🤖 Ask Copilot to investigate

Copy the prompt below into a new comment on this PR to delegate the investigation to GitHub Copilot. It will look into the flakiness and open a separate pull request with you as reviewer.

@copilot The following spec(s) are flaky in CI (first seen on PR #26191, linked for reference only):

- `rspec ./modules/bim/spec/features/viewer/create_viewpoint_spec.rb[1:2:1:1]`

Treat this as a standalone task, unrelated to PR #26191. Create a new branch from origin/dev and open a new pull request targeting dev — do not stack it on PR #26191 or reuse that branch.

Follow the playbook in docs/development/testing/handling-flaky-tests/README.md to find the root cause and fix the underlying race — do not skip, delete, or weaken the spec to make it pass; disabling is a last resort per the playbook, and only with a bug ticket. Verify the fix by running the spec(s) repeatedly (e.g. `script/bulk_run_rspec --run-count 10`).

If you cannot reproduce the flake or are not confident in a fix after reasonable investigation, do not fabricate a change or skip the spec to force CI green. Instead, leave the pull request in draft and document what you tried, the suspected cause, and any leads in its description, then assign @akabiru to take over.

Once the fix is verified, title the PR after the spec(s) it fixes, and use the PR description to explain the root cause, how the change resolves it, and the before/after results. Label the PR `flaky-spec`, assign @akabiru, and request a review from @akabiru.
On every commit, set @akabiru as the sole co-author with a `Co-authored-by:` trailer (use their GitHub no-reply email so it links to their account), so it is traceable who dispatched the fix.

The label row is gone when the journals are written, so the cause carries
the name alongside the id.

See COMMS-1067.
Writes one journal per affected work package, authored by the admin, with
the label name snapshotted into the cause since the label row is gone. The
journals are written directly, so no notifications go out for them.

See COMMS-1067.
The delete service collects the labeled work packages before the label goes
and hands them to the journaling job once the deletion is committed.

See COMMS-1067.
The XSS example only asserted the HTML variant; raw text must keep the name verbatim.
The guard subquery was re-run for every batch and could only scan the whole
journals table; one indexed lookup over the given work packages replaces it.
The epic defines the dialog text without a note about journaling.
A cause may now declare that it extends the predecessor's cause; the
aggregated journal then carries the merged cause. Deleting several labels
in a row leaves one entry per work package listing all of them.

See COMMS-1067.
Lists every label of an aggregated deletion and pluralises the verb.

See COMMS-1067.
Keeps the journal service free of duck-typing checks.
The cause line already names the removed labels.

See COMMS-1067.
Use the store accessor for the cause type and extract the predecessor cause aggregation into a named method.
@akabiru
akabiru force-pushed the implementation/comms-1067-journal-admin-label-deletion branch from 93a4f70 to f88a01a Compare October 21, 2026 14:10
Base automatically changed from implementation/comms-1022-labels-journaling to dev October 21, 2026 15:05

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai: Collaborative 💻 AI generated a substantial part of the code; A human reviewed and understands every line.

Development

Successfully merging this pull request may close these issues.

1 participant