Import bake-off · 59-row messy book + 4 controlled probes · a leading real estate CRM's native importer
The CRM's importer is a real matcher, not a dumb loader: it de-dupes on email and phone, and it knows Mike is Michael. But it only links records that share a strong identifier (email, mobile, work phone) and agree on the name, and when the name conflicts, it drops the record instead of queueing it (the import banner reports a count, never which rows or what they held). Everything with no shared identifier, and everything it can't reconcile, is where the resolver adds value. This is the honest delta, tested, not assumed.
phone column imported untyped, and the importer doesn't match on an untyped phone.Give it credit · then show where it stops
What it gets right
Shared email or shared mobile, with a matching or nickname name, merges cleanly, even Mike ⇔ Michael. A shared office line doesn't fuse strangers.
Parity with the resolver on the easy cases.
Where it goes blind
If the two records don't share an exact email or phone, the importer can't connect them, even when a nickname, a shared address, or a homophone makes it obvious to a person.
Resolver → same address + nickname, surfaced.
What it destroys
When a shared identifier meets a name it can't reconcile, the importer deletes the second record as "invalid", no review, no trace.
Resolver → keeps both, flags for review.
The scoreboard, tested
| Case | CRM importer | Resolver |
|---|---|---|
| Shared email / mobile + compatible name | merges | merges |
Untyped phone-column dup (basic CSV) | not matched | merges |
| No shared ID, complementary email-only + phone-only | misses | catches |
| Nickname / soundex with no shared ID | misses | catches |
| Gmail alias (m.cole@ = mcole+homes@) | not normalized | catches |
| Address as a match signal | never | corroborates |
| Name conflict on a shared ID (maiden→married) | deletes record | flags, keeps both |
| Spouse row → two people; non-person flag | mashup / as people | splits / flags |
| Recycled phone / family email → different people | avoided * | review, not auto |
* The importer's name-gate refuses to merge a shared number across different surnames; at test time (07-06) it was the more precise engine here, because the resolver's auto band merged both cases. Guards added 07-07 now demote them to review instead (details in the box below). Kept on the scoreboard because finding it was the test's job.
Two things we tested hard
The phone correction
A first pass concluded "email only." Four probes proved otherwise: identical-name and Mike/Michael pairs merged on a shared mobile; a same-name pair merged on a work number.
The 59-row file only showed email merges because its phone column came in untyped, the one case it genuinely won't match on.
Does the resolver scale?
auto-precision 0.50 → 1.00 · recall incl. review 0.29 → 0.71
A 28-row worst-case fixture false-merged a father/son (shared family email) and two strangers on a recycled phone. Both now demote to review (guards, 07-07); a nickname homophone and a hyphenated maiden name now surface too (07-09), and hygiene Layer 1 caught the typo'd email domain (07-10). A regression gate keeps it all fixed.
Still missed, on the record: name-order flips (Zhang Wei / Wei Zhang) and a nickname pair with zero shared identifiers; the real fix there is multi-value identifiers, on the roadmap.
The constructive half · email identity & hygiene, as layers · Layers 1 and 2 now built and live
Layer 1 · at import · instant, free · BUILT 07-10
Every domain validated against the full IANA real-TLD registry (snapshotted, offline) plus a hand-checked major-provider typo dictionary. Live: zero false flags on the 59-row import; caught gmial.com on the adversarial set, proposed gmail.com, and surfaced the hidden duplicate the typo was masking, for review, never auto-merged on an unconfirmed fix.
Guard: .co is a real TLD, never "corrected." Structural: the dictionary is only consulted after the TLD check fails.
Layer 2 · overnight · effectively free · BUILT 07-10
A raw MX lookup is a self-run DNS query: the whole protocol is ~80 lines of standard library, cached by domain. First live run caught a dead domain Layer 1 passes cleanly (real TLD, no typo) and verified Layer 1's proposed fix target accepts mail. A timeout is never flagged: infrastructure noise is not a data-quality accusation. Paid per-mailbox validation stays a narrow optional escalation.
Catches dead domains Layer 1's list can't. The layers corroborate.
Layer 3 · continuous · mailbox-native
Reconcile stored addresses against real correspondence: a silent saved address that closely matches a live orphaned one is the likely typo, and the live one is the truth. Precision comes from signals only a mailbox-native CRM has: bidirectional threads (reuses the CRM's relationship scoring), envelope display names, salutation/signature reads (a Maia-shaped job), thread co-occurrence.
A batch importer structurally can't do this. A mailbox-native CRM can.
One system, one rule throughout: propose, never silently rewrite, because a wrong email "fix" sends the next automated touch to the wrong human. It also answers the resolver's own admitted limit (people have several addresses; a good system should discover and union them from traffic).
The honest scoreboard
The CRM's importer is better than "dumb": strong-ID matching, nickname-aware, conservative about strangers. Its two real failures are reach (nothing links without a shared email or phone, no address, no soundex, no alias) and disposal (a name it can't reconcile gets deleted, not queued). The resolver's durable edge is the assisted, banded, non-destructive handling of that long tail, plus spouse/non-person structure, not a claim that it out-matches the CRM on strong identifiers.
And it isn't one-sided: this test caught the resolver's own auto band false-merging rare name-collisions that the CRM's name-gate avoids. The guards shipped the same week (demote to review, never discard) behind a regression gate, and both halves are published here because finding your own tool's failures is the point of running a bake-off instead of writing a comparison. The hygiene layers above are the constructive half: Layer 3 only works on a real mailbox, an advantage only a mailbox-native CRM has.