Repository navigation
fix(leads): keep the master lead when a merge lists it as a duplicate - #99
rudycelekli wants to merge 1 commit into
Conversation
POST /api/leads/dedup/merge fetched every id in duplicate_ids, merged them into the master and then deleted all of them. When the caller sends a whole cluster from /api/leads/dedup (which includes the master), the master was deleted along with the duplicates and the response still reported "merged". Leave the master out of the duplicates, merge a repeated id once, and reject a request whose only duplicate is the master. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Signed-off-by: Rudy Celekli <47457359+rudycelekli@users.noreply.github.com>
debpalash
left a comment
There was a problem hiding this comment.
The intended integer-ID cases are fixed, and I independently reproduced the reported 22 passing focused tests. All four GitHub checks are also green, including backend and PostgreSQL integrity/tenancy. However, the master can still be deleted through alternate representations of the same numeric ID; this is a blocking data-loss issue.
I added endpoint regression cases using duplicate master IDs represented as a leading-zero string, float, plus-prefixed string, and whitespace-padded string. All eight combinations (four inputs × SQLite LeadDB and PgLeadStore backed by SQLite) failed because the request returned 200 and the master was deleted. The two original tests do not cover repeated duplicate IDs or alternate representations; the ORM fixture tests the ORM implementation but not a real PostgreSQL database.
The correction must compare the canonical IDs returned by the store before appending to the deletion list, and deduplicate those canonical IDs. Cover aliases of both master_id and duplicate_ids, repeated IDs, master-only aliases, empty inputs, and missing duplicates. I implemented that correction and all 40 expanded focused tests pass. Because the integration cannot write to the contributor fork, #109 preserves your original commit and adds the fix and regression coverage. Please incorporate that correction before this PR is merged.
| seen = {str(master_id)} | ||
| unique_duplicate_ids = [] | ||
| for duplicate_id in duplicate_ids: | ||
| if str(duplicate_id) not in seen: |
There was a problem hiding this comment.
[P1] Compare resolved lead identities rather than str(input). For master_id=1, duplicate_ids=["01", 2] passes this filter, but both stores in the regression fixture resolve "01" to master 1. The subsequent deletion loop deletes master 1 and returns 200. I reproduced this for "01", 1.0, "+1", and " 1 " in both fixtures (8 failures). Deduplicate and exclude the master using IDs from fetched lead records. Correction and expanded tests are published in #109.
Summary
POST /api/leads/dedup/mergefetched every id induplicate_ids, merged them into the master and then deleted all of them. Ifmaster_idwas also listed (for example a caller sends a whole cluster from/api/leads/dedup, whoseclustersinclude the master), the master was deleted along with the duplicates and the response still said"merged", withduplicates_deletedcounting the master. The web UI sends themerge_suggestionsids, which exclude the master, so it is not affected; API and agent callers are.Evidence
New
tests/test_lead_dedup_merge.py(runs against both the SQLiteLeadDBand the ORMPgLeadStore), on currentmain:Change
The master is dropped from the duplicates, a repeated id is merged once, and a request whose only duplicate is the master gets a 400 instead of deleting it.
merge_leadsis untouched.Verification
uv run pytest tests/test_lead_dedup_merge.py tests/test_dedup.py tests/test_lead_create_collision.pypasses (22 tests).Commit is signed off per the DCO.