fix(manage_conversation_labels): restrict add to existing catalog labels - #56
Conversation
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.
Reviewer's GuideThe add flow now loads the account’s configured label catalog, applies only case-insensitive matches using canonical titles, reports skipped titles via Sequence diagram for catalog-validated conversation label additionsequenceDiagram
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
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
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
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
- _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.
|
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. |
Summary
Conversation tagging (
acts_as_taggable_on :labels) accepts any free-form string, with no relation to the account's actualLabelmodel (Settings > Labels).manage_conversation_labels'saddaction 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
addnow 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 newrejectedfield 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: errorwith 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:
Enhancements: