Import bake-off · 59-row messy book + 4 controlled probes · a leading real estate CRM's native importer

What a leading CRM's importer actually does with a messy book,
and where an assisted resolver still earns its place.


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.

Merge = a shared identifier + a compatible name. No shared identifier, no link. Name conflict? It drops the record.
Proven across four import runs. On the 59-row file only email-merges fired, because its one phone column imported untyped, and the importer doesn't match on an untyped phone.
59
rows in
messy client book
3
dropped "invalid"
incl. 1 real person
5
auto-merged
exact-email, some lossy
51
contacts created
~8 dupes still floating

Give it credit · then show where it stops

What it gets right

Strong-ID matches, nickname-aware.

Shared email or shared mobile, with a matching or nickname name, merges cleanly, even Mike ⇔ Michael. A shared office line doesn't fuse strangers.

Mike / Michael Larkinsame mobile → merged
David / Davdi Nguyensame email → merged

Parity with the resolver on the easy cases.

Where it goes blind

No shared identifier, no link.

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.

Bob Jensenphone only, 30 River Rd
Robert Jensenemail only, 30 River Rd

Resolver → same address + nickname, surfaced.

What it destroys

Name conflict → dropped, not flagged.

When a shared identifier meets a name it can't reconcile, the importer deletes the second record as "invalid", no review, no trace.

Jennifer Choi → Adamsmarried name deleted
Dwight / Dwayne (same #)Dwayne deleted

Resolver → keeps both, flags for review.

The scoreboard, tested

CaseCRM importerResolver
Shared email / mobile + compatible namemergesmerges
Untyped phone-column dup (basic CSV)not matchedmerges
No shared ID, complementary email-only + phone-onlymissescatches
Nickname / soundex with no shared IDmissescatches
Gmail alias (m.cole@ = mcole+homes@)not normalizedcatches
Address as a match signalnevercorroborates
Name conflict on a shared ID (maiden→married)deletes recordflags, keeps both
Spouse row → two people; non-person flagmashup / as peoplesplits / flags
Recycled phone / family email → different peopleavoided *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

It does merge on phone, mobile and work.

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?

Adversarial data broke it; the fixes are in.

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

Propose the obvious typo fix.

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

Ask DNS if the domain takes mail.

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

Mail traffic is the tie-breaker.

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.

Empirical, 2026-07-06; updated 2026-07-10 · 59-row fixture + 4 phone probes through the CRM's live importer · resolver stress fixture now 28 rows; guards (07-07) + recall pass (07-09) + hygiene Layers 1-2 (07-10) applied and gated · detail: identity-resolution-and-hygiene.md · every finding traceable to a source row · Brokerage CDP · Layer 2