One Customer, Six Systems: The Confidently Wrong Match
I connected six places where a B2B company keeps customer context: chat, tickets, wiki, CRM, mailboxes and meeting notes. The first version searched all six and returned grounded answers. Useful, and it did not remove the real work. Someone still had to notice that a ticket, a CRM account and a meeting note referred to the same customer, decide which source was current, and copy the change to whoever needed it.
That turned out to be a mapping problem more than a retrieval problem, and the mapping problem has a failure mode that no queue catches.
The cases are ordinary
The difficult cases are not exotic:
- a company gets renamed;
- a parent and a subsidiary share contacts;
- people use a product name as the customer name;
- the CRM says active while the latest ticket says blocked;
- the same decision appears in chat and in the meeting notes with different wording.
Search can return all of that. It cannot tell you what belongs together or who should know.
The confidently wrong match
Schema drift was never the thing that hurt. What hurt was the agent being certain which customer a record belonged to. Every field validates, the fingerprint matches, and the update lands on the wrong account at full confidence. There is no 429 and no dead-letter queue for that one. It goes quietly to the wrong team.
Ambiguous cases can be reviewed. The confident wrong match is the expensive one precisely because nothing in the pipeline asks for a second look. What stopped it was resolving the canonical entity ahead of the write path rather than after retrieval: every record folds under a customer registry before anything is proposed, and the proposal names the customer it was filed against.
Which source wins on which field
Recency by default got me into trouble fast, so authority is per field now. The rule I landed on: the system where a field is authored beats every system that mirrors it. A ticket is written by the customer, so it wins on the state of the issue. The CRM stage is written by the person who owns the account, so it wins on commercial state. When the CRM says active and the ticket says blocked, those are not in conflict. They are two different fields, and modelling them as one was the mistake.
Facts with a start and an end
Facts are recorded with a valid-from and, when it comes, an invalid-from. When a new fact states the opposite of an existing one, the older fact is invalidated. That covers direct contradictions and nothing else, and the hole is exact: a scope change keeps the subject, the predicate and the type intact, so nothing fires. “Pricing is X” becomes wrong when X changes scope, not value.
What saves that case is that the source and the quoted passage stay attached to the fact. The drift is visible in the quote even when the triple looks unchanged. That is a human catch, not an automatic one, and it is the reason the passage travels with every record instead of being summarised away.
A correction has to teach something
Today a correction is written back as an alias on the canonical entity, so that exact pair never breaks again. That repairs one incident and teaches the matcher nothing. The scoring underneath stays as naive as it was on day one while the aliases pile up.
The better version treats corrections as labelled data: the original context and the corrected customer as a positive example, the wrongly chosen customer as a hard negative, and the matcher recalibrated in batches. I have not gone there yet because hard negatives from a small team are thin and biased toward whatever the team happened to notice. So corrections are held as a labelled set until there are enough to recalibrate against, and the aliases carry the load until then.
Silence is not decay
A source that goes quiet is not a source that went wrong. Quiet usually means nobody touched it. I would rather hold a stale fact with a visible date than a confident one that faded on a timer. What a quiet source should do is lower the ceiling: a fact sourced from something that stopped updating cannot stay at full confidence forever, it just should not drop on a schedule. The piece to build next is re-assertion on read: when a fact is used, the source is checked, and the date on the fact moves or the fact is flagged.
What this means for control
Matching is a context problem with a control consequence. A wrong match routes a write to the wrong customer, and the only place a person still looks before that write is the approval card. So the card names the resolved customer, quotes the passage, and shows the previous value. The confidently wrong match becomes visible at the one step that already has a person in it, which is the whole point of putting the person there.