Refs #223 — the half that can move while the matching question is still open.
Mozilla's schema defines no UID, so the LDIF import invents one and a re-import duplicates everything. Whether to guess an identity from a name and an address instead is the reporter's call, and he has not made it.
But the harm actually reported was confusion, not duplication — somebody imports a file twice and cannot tell what happened. That part can be answered without answering the other.
What it does
Counts how many entries look like contacts the book already held, and says so in a second toast after the import result. Every card is still imported. Nothing skipped, nothing merged.
Counting is a different act from matching. It takes no decision away from the person who still owes us one — and if the answer comes back "match on name and email", the matching is now written and becomes a skip instead of a count.
The likeness key, and why it is safe here
Name plus one address, case- and whitespace-normalised, one key per address so a match on a second address still counts.
It is wrong in both directions by design: two colleagues sharing a name and a shared alias collapse into one, and somebody whose address changed since the last export looks like a stranger. That is tolerable for a number on a toast and would not be tolerable for a merge — which is exactly why the number is all it does. The reasoning is in the code beside it, so nobody later mistakes it for a matcher.
Cost
None extra. The scan the vCard import already makes for UIDs now collects names and addresses on the same ContactCard/get, so it is a wider properties list on a request already being made. It is read before anything is created, so a file that repeats a person twice counts as two new cards rather than as a duplicate of itself.
Tests
Ten new: a name+address match counted, the card imported anyway, a name match with a different address not counted, an address match under a different name not counted, a match on a second address, case and spacing normalised, each entry counted once however many addresses match, other books ignored, an empty book, and a file that repeats a person not counted against itself.
Existing expectations move to the three-field shape. npm run typecheck, npm test (987 web + 126 server), npm run build, i18n:check pass. One new plural in all nine catalogues.
Refs #223 — the half that can move while the matching question is still open.
Mozilla's schema defines no UID, so the LDIF import invents one and a re-import duplicates everything. Whether to guess an identity from a name and an address instead is the reporter's call, and he has not made it.
But the harm actually reported was **confusion**, not duplication — somebody imports a file twice and cannot tell what happened. That part can be answered without answering the other.
## What it does
Counts how many entries look like contacts the book already held, and says so in a second toast after the import result. **Every card is still imported.** Nothing skipped, nothing merged.
Counting is a different act from matching. It takes no decision away from the person who still owes us one — and if the answer comes back "match on name and email", the matching is now written and becomes a skip instead of a count.
## The likeness key, and why it is safe here
Name plus one address, case- and whitespace-normalised, one key per address so a match on a second address still counts.
It is **wrong in both directions by design**: two colleagues sharing a name and a shared alias collapse into one, and somebody whose address changed since the last export looks like a stranger. That is tolerable for a number on a toast and would not be tolerable for a merge — which is exactly why the number is all it does. The reasoning is in the code beside it, so nobody later mistakes it for a matcher.
## Cost
None extra. The scan the vCard import already makes for UIDs now collects names and addresses on the same `ContactCard/get`, so it is a wider `properties` list on a request already being made. It is read *before* anything is created, so a file that repeats a person twice counts as two new cards rather than as a duplicate of itself.
## Tests
Ten new: a name+address match counted, the card imported anyway, a name match with a different address *not* counted, an address match under a different name *not* counted, a match on a second address, case and spacing normalised, each entry counted once however many addresses match, other books ignored, an empty book, and a file that repeats a person not counted against itself.
Existing expectations move to the three-field shape. `npm run typecheck`, `npm test` (987 web + 126 server), `npm run build`, `i18n:check` pass. One new plural in all nine catalogues.
**Merged** 2026-09-02 as coffey-labs/ihasmail@a8cf8ce3d796
<sub>Rebuilt from: git history, session transcript.</sub>
This repo is archived. You cannot comment on issues.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Refs #223 — the half that can move while the matching question is still open.
Mozilla's schema defines no UID, so the LDIF import invents one and a re-import duplicates everything. Whether to guess an identity from a name and an address instead is the reporter's call, and he has not made it.
But the harm actually reported was confusion, not duplication — somebody imports a file twice and cannot tell what happened. That part can be answered without answering the other.
What it does
Counts how many entries look like contacts the book already held, and says so in a second toast after the import result. Every card is still imported. Nothing skipped, nothing merged.
Counting is a different act from matching. It takes no decision away from the person who still owes us one — and if the answer comes back "match on name and email", the matching is now written and becomes a skip instead of a count.
The likeness key, and why it is safe here
Name plus one address, case- and whitespace-normalised, one key per address so a match on a second address still counts.
It is wrong in both directions by design: two colleagues sharing a name and a shared alias collapse into one, and somebody whose address changed since the last export looks like a stranger. That is tolerable for a number on a toast and would not be tolerable for a merge — which is exactly why the number is all it does. The reasoning is in the code beside it, so nobody later mistakes it for a matcher.
Cost
None extra. The scan the vCard import already makes for UIDs now collects names and addresses on the same
ContactCard/get, so it is a widerpropertieslist on a request already being made. It is read before anything is created, so a file that repeats a person twice counts as two new cards rather than as a duplicate of itself.Tests
Ten new: a name+address match counted, the card imported anyway, a name match with a different address not counted, an address match under a different name not counted, a match on a second address, case and spacing normalised, each entry counted once however many addresses match, other books ignored, an empty book, and a file that repeats a person not counted against itself.
Existing expectations move to the three-field shape.
npm run typecheck,npm test(987 web + 126 server),npm run build,i18n:checkpass. One new plural in all nine catalogues.Merged 2026-09-02 as coffey-labs/ihasmail@a8cf8ce3d7
Rebuilt from: git history, session transcript.