Skip to content

[COMMS-1071] Find or create labels when the work package is saved - #26270

Merged
akabiru merged 26 commits into
devfrom
implementation/comms-1071-create-labels-on-work-package-save
Oct 28, 2026
Merged

akabiru merged 26 commits into
devfrom
implementation/comms-1071-create-labels-on-work-package-save

Conversation

@akabiru

@akabiru akabiru commented Oct 23, 2026 •

Copy link
Copy Markdown
Member

Ticket

https://community.openproject.org/wp/81938

What are you trying to accomplish?

Make labels typed into the form now stay local until the work package is saved. The autocompleter keeps them as pending items, the payload sends each one as a _links.labels entry with a null href and the name as title, and the label is found or created in the same transaction as the work package.

{
  "_links": {
    "labels": [
      { "href": "/api/v3/labels/130" },
      { "href": "/api/v3/labels/131" },
      { "href": "/api/v3/labels/132" },
      { "href": null, "title": "Needs triage" } // new label, created when the work package saves
    ]
  },
  "_meta": { "validateCustomFields": true },
  "lockVersion": 1
}
Screen.Recording.2026-10-27.at.6.31.34.PM.mov

What approach did you choose and why?

Creating labels is service work, so the work package create and update services find or create the named labels as the acting user inside the transaction they already open; a failed save leaves no label behind. Names are created in a stable order so that two saves introducing the same names cannot deadlock on the unique name index, and a label created concurrently under the same name is reused instead of failing the save. The model itself only refuses to save while names are still unresolved, so nothing can drop them silently.

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, ...)

Closes #26187

A work package can carry label names next to label ids. The names are
resolved or created inside the save transaction, so an abandoned form
leaves no label behind and the contract rejects names the user may not
create before anything is written.
A `_links.labels` entry with a null href and a title is a label to
create on save; the form endpoints echo it back without persisting.
The autocompleter keeps a new label as a local pending item and the
work package payload sends it as a titled link without href.
@akabiru akabiru self-assigned this Oct 23, 2026
Comment thread app/models/concerns/labelable.rb Fixed
The form echo and the contract looked a label up per name; both now
match all names at once with the case folding done in Postgres.
@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Deploying openproject with ⚡ PullPreview

Field Value
Latest commit d8bd61e
Job deploy
Status ✅ Deploy successful
Preview URL https://pr-26270-comms-1071-creat-ip-188-34-156-127.my.opf.run:443

View logs

@github-actions

Copy link
Copy Markdown

Warning

Flaky specs

  • rspec ./spec/features/work_packages/cancel_editing_spec.rb[1:5]
🤖 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 #26270, linked for reference only):

- `rspec ./spec/features/work_packages/cancel_editing_spec.rb[1:5]`

Treat this as a standalone task, unrelated to PR #26270. Create a new branch from origin/dev and open a new pull request targeting dev — do not stack it on PR #26270 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 create service ignored the result of its unvalidated save, so an aborted
label creation needed a raise to avoid a silent success and ended in a 500.
Honouring the save result turns it into the same 422 the update path gives.
Names that already exist are filtered in one query, so uniqueness and the
permission lookup no longer run once per pending name on every form refresh.
A pending label and a persisted label with the same name are the same label,
since the backend identifies labels by their lower-cased name.
Without a labels link on the reference the base changeset writes raw resources
into the payload and the pending titles were lost.
Running the model's own validation keeps the contract in step with
Label; the cap bounds how many labels one save can create.
Every item carries a name, so the href fallback could never run.
POST /api/v3/labels is hidden behind the flag, so saving a work package
must not create labels or change labelings either.
Creating another aggregate's records as the acting user is service work;
the model only refuses to save while names are unresolved. Names are
created in a stable order so concurrent saves cannot deadlock.
API clients need the permission, length and per-request limits to
handle the 422 responses.
A shared work package grants edit rights without the project-wide right
to create labels, so updates and their form must reject new names.
Copying only the error symbols dropped the length limit, so rendering
a too-long label name raised instead of answering with a 422.
FindOrCreateLabels says that typed names become existing or new labels;
pending and resolve left the reader guessing.
Labels are now created when the work package saves, so a bad name must
come back as a field error in split view, the table and the create form.
No permission is called create labels; the check is edit rights in any
project, the same as POST /api/v3/labels.
Pending said nothing about what the names are for; the checks validate
label names and pick out the ones that would create labels.
The label ids and names are what the next save assigns, which pending
did not say.
@github-actions

Copy link
Copy Markdown

Warning

Flaky specs

  • rspec ./modules/documents/spec/features/documents/project/show_edit_document_spec.rb[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 #26270, linked for reference only):

- `rspec ./modules/documents/spec/features/documents/project/show_edit_document_spec.rb[1:1]`

Treat this as a standalone task, unrelated to PR #26270. Create a new branch from origin/dev and open a new pull request targeting dev — do not stack it on PR #26270 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.

Mirrors Attachments::ValidateReplacements so the work package contract
does not carry label rules inline.
@github-actions github-actions Bot added the ai: Collaborative 💻 AI generated a substantial part of the code; A human reviewed and understands every line. label Oct 27, 2026
@akabiru akabiru added this to the 18.0.x milestone Oct 27, 2026
@akabiru
akabiru requested review from a team, HDinger and brunopagno October 27, 2026 15:54
@akabiru
akabiru marked this pull request as ready for review October 27, 2026 15:54
@github-actions

Copy link
Copy Markdown

Warning

Flaky specs

  • rspec ./modules/documents/spec/features/documents/project/show_edit_document_spec.rb[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 #26270, linked for reference only):

- `rspec ./modules/documents/spec/features/documents/project/show_edit_document_spec.rb[1:1]`

Treat this as a standalone task, unrelated to PR #26270. Create a new branch from origin/dev and open a new pull request targeting dev — do not stack it on PR #26270 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.

Labels are resolved on their own line, like attachments, so the save no
longer reads as depending on them; the post-save steps move out to keep
the method within the complexity limit.
The after_validate hook is the slot for work that needs a passed
contract but no save yet, so persist stays the plain save.
Matches the server, which treats a title-only label link as a new
record.
@akabiru akabiru changed the title [COMMS-1071] Create labels when the work package is saved [COMMS-1071] Find or create labels when the work package is saved Oct 27, 2026

@brunopagno brunopagno left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To me this looks pretty good. I think this is the better approach. I really appreciate the effort of making it 🙇

module Labelable
extend ActiveSupport::Concern

MAX_LABEL_NAMES_PER_SAVE = 50

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels like a check we would add after something broke 😅
Maybe it's good that we're preemptive. I don't disagree with validating the amount.

Any special reason for 50?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Arbitrary limit tbh, not sure it's practical to allow 1000 labels? 😄 Happy to drop it though 👍🏾

@brunopagno brunopagno Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The UI does not really allow someone to add 1000 labels. But maybe the API would. I guess it's a problem that is a bit too abstract, I would not expect users to "abuse" this system too much. But I could be wrong

In any case, I think it's fine the way it is. My initial gut feeling was to make the limit stricter, not more lenient 😅

50 is fine 👍

@akabiru

akabiru commented Oct 28, 2026

Copy link
Copy Markdown
Member Author

Verbal Ok from Henriette on the new pattern, merging 👍🏾

@akabiru
akabiru merged commit 0dd537b into dev Oct 28, 2026
27 checks passed
@akabiru
akabiru deleted the implementation/comms-1071-create-labels-on-work-package-save branch October 28, 2026 12:01
@github-actions github-actions Bot locked and limited conversation to collaborators Oct 28, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

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.

3 participants