Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Use when collapsing or linking records that point to the same real-world person, company, or account but share no common id — combining overlapping customer lists, de-duplicating a contact table full of near-duplicates, or joining two systems that lack a shared key. Chooses candidate buckets to cut comparisons, matches with field-aware similarity, sets a confidence cutoff with a review band, and merges survivors without discarding data. Do NOT use for mapping mentions to a KNOWN canonical id registry (that is entity-normalization) or for a plain keyed join on an existing shared column.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 559% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 5397% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 580% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 574% | 0% |
| case-12 | ✗→✗ | = Same ✗ | 586% | 0% |
Two rows can describe one real customer while agreeing on nothing exactly: Bob vs Robert, (415) 555-0100 vs 415.555.0100, a maiden name vs a married one, an email present on one side only. There is no id to join on — sameness must be inferred. Shown this task, the base model tends to (a) test rows for exact-string equality and miss the real duplicates, and (b) when it does collapse a pair, keep one row and throw the rest away, silently losing every field that lived only on the discarded rows. This skill supplies the discipline that prevents both: bucket to make the problem tractable, match on normalized field-aware similarity, gate the merge on confidence with a middle band that goes to a human, and combine survivors so no value is lost.
Not this skill: resolving mentions to a fixed in-house id set (entity-normalization), or joining on a key that already exists (an ordinary keyed join).
A list of N rows has ~N²/2 possible pairs; at 200k rows that is tens of billions of comparisons. Cut it: derive a cheap grouping value that two versions of the same entity will almost always agree on, put rows sharing that value into the same bucket, and only compare rows inside a bucket.
last 7 phone digits, an email's domain, a Soundex/metaphone code of the name.
Run more than one pass with different values and union the candidate pairs each pass proposes.
you actually see in the data.
Normalize each field to a canonical form BEFORE comparing, then score each field with a metric that fits it. Never test raw text for equality.
| Field | Normalize | Compare with | |---|---|---| | Person name | case-fold, strip punctuation, expand nickname → formal (Bob→Robert) | Jaro-Winkler / edit distance; phonetic for spelling drift | | Company name | drop legal suffixes (Inc, LLC, Ltd), case-fold, strip punctuation | token-set / edit distance | | Address | expand abbreviations (St→Street, Ave→Avenue), standardize unit/zip | token similarity on the normalized string | | Phone | strip formatting, drop country code, keep the local digits | equality on the normalized digits | | Email | lower-case, trim | equality on the normalized value |
Full tables: see references/nickname-and-address-tables.md for the nickname→formal dictionary, the USPS street-suffix / directional / unit-designator abbreviations, and the legal-entity suffixes — the body covers the method; the specific entry to expand is a lookup.
Combine the per-field scores into one match score (a weighted sum, or a simple rule such as "two strong fields agree"). One shared field is weak evidence: dozens of distinct people share a common name, so a name-only match over-collapses. Require corroboration from a second discriminating field (email, phone, address) before calling a pair the same.
Do not use a single yes/no line. Set a HIGH cutoff and a LOW cutoff:
The band exists because a wrong merge fuses two real customers into one — costly and hard to undo. When unsure, defer rather than guess.
Matching yields pairs. If A links to B and B links to C, resolve A, B, C into ONE entity even when A and C were never directly compared or don't look alike. Group connected pairs into clusters (connected components); each cluster becomes one merged entity.
A merge builds ONE surviving "golden" record from a cluster. It is not "keep the first row." For each field, pick the surviving value by an explicit rule, and retain what you don't pick:
cluster member.
newer updated_at / timestamp.
one (self-entered web form), the trusted source wins regardless of recency.
history / an alternate, not deleted. Record which source each surviving value came from.
State which rule you used per field; when rules conflict, say which took precedence and why.
Emit, at minimum:
Two goals share this machinery. Merge collapses duplicates within/across sets into one golden record (survivorship applies). Linkage leaves both systems' rows in place and just records a correspondence (a crosswalk / foreign-key link) so downstream joins work with no shared column — here you produce links and a review queue, but you do NOT overwrite either side's fields.
demand a second corroborating field.
distinct people; a shared address plus surname alone is not sufficient to merge.
entity; missing fields are absence of evidence, not evidence of difference.
priority, then to the review queue; do not pick arbitrarily.
confirmed by the other."
A good execution of this skill should:
Other measured skills in the registry, with their headline benchmark lift.