Skip to content

fix(manage_conversation_labels): restrict add to existing catalog labels - #56

Open
mateusbellozupko wants to merge 2 commits into
evolution-foundation:developfrom
mateusbellozupko:fix/manage-labels-catalog-restriction
Open

fix(manage_conversation_labels): restrict add to existing catalog labels#56
mateusbellozupko wants to merge 2 commits into
evolution-foundation:developfrom
mateusbellozupko:fix/manage-labels-catalog-restriction

Conversation

@mateusbellozupko

@mateusbellozupko mateusbellozupko commented Aug 29, 2026

Copy link
Copy Markdown

Summary

Conversation tagging (acts_as_taggable_on :labels) accepts any free-form string, with no relation to the account's actual Label model (Settings > Labels). manage_conversation_labels's add action let the model tag conversations with invented label titles that never existed in that catalog — invisible in Settings, uncolored, and absent from any label-based filter.

Fix

add now fetches the account's label catalog (GET /api/v1/labels) and only applies titles that already exist there (case-insensitive match). Anything else is skipped and reported back via a new rejected field instead of silently creating an ad-hoc tag. Docstring updated to tell the model not to invent label titles.

Testing notes

Verified against a live conversation: adding a label whose title didn't exist in the account's catalog now returns status: error with the rejected title listed, instead of tagging the conversation with a string that would never show up in Settings > Labels.

🤖 Generated with Claude Code

Summary by Sourcery

Ensure conversation labels can only be applied from the account’s configured label catalog and clearly report rejected requests.

Bug Fixes:

  • Restrict conversation label additions to labels already present in the account catalog, using case-insensitive matching.
  • Report skipped non-catalog labels through the add response instead of creating invisible ad-hoc tags.

Enhancements:

  • Handle label catalog fetch failures separately from an empty catalog and update tool guidance to prevent invented label titles.

Conversation tagging (acts_as_taggable_on) accepts any free-form string
with no relation to the account's actual Label model (Settings > Labels).
The tool was letting the model tag conversations with invented label
titles that never existed in that catalog — invisible in Settings,
uncolored, and absent from any label-based filter.

`add` now fetches the account's label catalog and only applies titles
that already exist there (case-insensitive match); anything else is
skipped and reported back via `rejected` instead of silently tagging the
conversation with an ad-hoc string.
@sourcery-ai

sourcery-ai Bot commented Aug 29, 2026

Copy link
Copy Markdown

Reviewer's Guide

The add flow now loads the account’s configured label catalog, applies only case-insensitive matches using canonical titles, reports skipped titles via rejected, and surfaces catalog lookup failures; the tool documentation and response messaging explain the restriction.

Sequence diagram for catalog-validated conversation label addition

sequenceDiagram
    participant Tool as manage_conversation_labels
    participant CRM as EvoCrmClient
    participant Catalog as Label catalog
    participant Conversation as Conversation labels

    Tool->>CRM: get(endpoint=/labels, params={per_page: 200})
    CRM-->>Tool: catalog titles
    Tool->>Tool: case-insensitive catalog matching
    alt Catalog lookup fails
        Tool-->>Tool: return status=error
    else Requested labels include rejected titles
        Tool->>Conversation: update with valid canonical titles
        Conversation-->>Tool: resulting labels
        Tool-->>Tool: return rejected titles
    else All requested labels are valid
        Tool->>Conversation: update with valid canonical titles
        Conversation-->>Tool: resulting labels
    end
Loading

File-Level Changes

Change Details Files
Validate labels against the account catalog before applying conversation tags.
  • Fetch up to 200 catalog labels from the labels API.
  • Match requested titles case-insensitively and preserve the catalog’s canonical casing.
  • Skip unknown titles and return them in a new rejected field.
  • Handle catalog-fetch failures as errors instead of rejecting all requests silently.
src/services/adk/tools/evo_crm/manage_conversation_labels.py
Expose partial and rejected-add outcomes clearly to callers and guide model behavior.
  • Update success and error messages for skipped or entirely rejected labels.
  • Document that add only accepts labels configured in Settings > Labels.
  • Document the rejected response field.
src/services/adk/tools/evo_crm/manage_conversation_labels.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 2 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="src/services/adk/tools/evo_crm/manage_conversation_labels.py" line_range="282-291" />
<code_context>
+                else:
+                    rejected.append(label)
+
+            if rejected and not catalog_labels:
+                # Catalog fetch failed — don't silently reject everything;
+                # surface the failure instead of pretending no labels exist.
+                return {
+                    "status": "error",
+                    "message": (
+                        "Could not load the account's label catalog to validate the "
+                        "requested label(s), so nothing was added. Please retry."
+                    ),
+                    "conversation_id": effective_conversation_id,
+                    "action": "add",
+                }
</code_context>
<issue_to_address>
**issue (bug_risk):** An account with a legitimately empty label catalog is indistinguishable from a failed catalog fetch because both produce `catalog_labels == []`; the function returns a generic catalog-load error without the required `rejected` field, so the requested titles are not reported back as rejected.

**Triggers:** When the account has no configured labels and an `add` request is made.

**Suggested fix:** Track fetch success separately from the returned list, and include the requested titles in `rejected` for a successfully loaded empty catalog.
</issue_to_address>

### Comment 2
<location path="src/services/adk/tools/evo_crm/manage_conversation_labels.py" line_range="304-310" />
<code_context>
+                message = "All requested labels were already present; nothing to update."
</code_context>
<issue_to_address>
**nitpick (bug_risk):** When at least one requested label is already present and the remaining requested labels are rejected, the `message` says that none of the requested labels exist in the catalog, even though the already-present label was validated successfully; this gives the caller a false explanation of what happened.

**Triggers:** When an add request mixes an already-present catalog label with one or more titles absent from the catalog.

**Suggested fix:** Use a mixed-result message that distinguishes already-present valid labels from rejected titles.

```suggestion
                message = "All requested labels were already present; nothing to update."
                if rejected and valid_requested:
                    message = (
                        f"Requested label(s) already present: {', '.join(valid_requested)}. "
                        f"Rejected label(s) not found in the account's label catalog: "
                        f"{', '.join(rejected)}. Only pre-existing labels can be applied — "
                        f"create them in Settings > Labels first."
                    )
                elif rejected:
                    message = (
                        f"None of the requested label(s) exist in the account's label "
                        f"catalog: {', '.join(rejected)}. Only pre-existing labels can "
                        f"be applied — create them in Settings > Labels first."
                    )
```
</issue_to_address>

Sourcery assessment

Needs a human reviewer. 1 finding to address first, and a catalog-fetch or matching defect could reject valid labels or write a wrongly normalized label to a conversation. Those changes are persisted, but the affected labels are bounded and can be removed or corrected; reverting restores the prior tagging behavior.

Blocking findings: src/services/adk/tools/evo_crm/manage_conversation_labels.py:291


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/services/adk/tools/evo_crm/manage_conversation_labels.py Outdated
Comment thread src/services/adk/tools/evo_crm/manage_conversation_labels.py Outdated
- _fetch_catalog_labels now returns (fetch_succeeded, titles), so a
  legitimately empty label catalog is no longer indistinguishable from a
  failed fetch.
- Fixed the add-result message to distinguish "already present" from
  "rejected" instead of claiming none of the requested labels existed
  when some were already attached to the conversation.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Sourcery assessment

Approved.

@mateusbellozupko

Copy link
Copy Markdown
Author

Fixed in 8135358 — _fetch_catalog_labels now returns (fetch_succeeded, titles) so an empty catalog and a failed fetch are distinguishable, and the add-result message no longer claims none of the requested labels existed when some were already present.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant