Seven names exist as two or more separate people on both devices. For example, Ian with 756 faces and Ian with 4, Jessie three times, PJ and Pascal. They were most likely typed separately on each device and then carried across by sync. There are also pairs of faces that are the same face held twice, where two detections of one face landed in the same photograph. There were none on the reference catalogs on 2026-09-26, but nothing prevents them.
Why
A person split in two shows half their photographs under each name, and every confirmation made on one half is invisible to the other. It is the "parallel libraries" failure again, this time at the level of people rather than faces.
Deliverable
A deduplication job that:
merges people who have the same name (trimmed, case-insensitive) and whose faces agree by embedding: the centroids, or the best pairwise agreement of their confirmed faces, above a threshold measured on the real catalog. Same-name people whose faces disagree stay apart and are reported;
removes duplicate face rows: the same image and embedder, IoU ≥ 0.5 and cosine ≥ the #77 threshold. It keeps the face from the stronger detector (FaceDetector::outranks), and moves the confirmed assignment and any rejections onto the kept face;
is idempotent, runs without the user having to ask (after a sync merge and/or on open, if it's cheap), and logs what it did;
propagates: the other device must end up with the same people, including a device on the previous release. A job that merges on one side only, to have sync send the duplicate back, is worse than none.
Acceptance
Measured on copies of the desktop catalog and the server snapshot: which same-name groups merge and which stay apart, with their similarity figures
Confirmations, rejections and names survive a merge, and a test covers each
A merge made on one device survives a sync round trip with an unchanged peer (a test with two catalogs)
No schema bump
**Seven names exist as two or more separate people on both devices.** For example, Ian with 756 faces and Ian with 4, Jessie three times, PJ and Pascal. They were most likely typed separately on each device and then carried across by sync. There are also pairs of faces that are the same face held twice, where two detections of one face landed in the same photograph. There were none on the reference catalogs on 2026-09-26, but nothing prevents them.
## Why
A person split in two shows half their photographs under each name, and every confirmation made on one half is invisible to the other. It is the "parallel libraries" failure again, this time at the level of people rather than faces.
## Deliverable
A deduplication job that:
- merges people who have the same name (trimmed, case-insensitive) and whose faces agree by embedding: the centroids, or the best pairwise agreement of their confirmed faces, above a threshold measured on the real catalog. Same-name people whose faces disagree stay apart and are reported;
- removes duplicate face rows: the same image and embedder, IoU ≥ 0.5 and cosine ≥ the #77 threshold. It keeps the face from the stronger detector (`FaceDetector::outranks`), and moves the confirmed assignment and any rejections onto the kept face;
- is idempotent, runs without the user having to ask (after a sync merge and/or on open, if it's cheap), and logs what it did;
- propagates: the other device must end up with the same people, including a device on the previous release. A job that merges on one side only, to have sync send the duplicate back, is worse than none.
## Acceptance
- [ ] Measured on copies of the desktop catalog and the server snapshot: which same-name groups merge and which stay apart, with their similarity figures
- [ ] Confirmations, rejections and names survive a merge, and a test covers each
- [ ] A merge made on one device survives a sync round trip with an unchanged peer (a test with two catalogs)
- [ ] No schema bump
dr_catalog::dedup_people::run runs after every sync merge, on the sync worker, in one transaction:
People with the same name (trimmed, case-folded) merge into the one with the most confirmed faces when every shared embedder's confirmed-face centroids agree at cosine ≥ 0.7, i.e. distance < 0.3 as requested. A namesake with no faces merges outright. Each side needs ≥ 2 confirmed faces to compare. A face confirmed as one and rejected as the other keeps them apart. Unnamed people are never touched.
Face rows held twice (same image and embedder, IoU ≥ 0.5, cosine ≥ 0.7) fold into the stronger detector's face, with the assignment and rejections carried over.
Propagation uses the existing merged_into redirect, which 0.17.0 peers already honour. Verified by a two-catalog test and on the real desktop/tablet pair, both converging on the same people.
On the reference library: Claudine, Jessie ×2, Mathias and Noemi merge (plus the tablet's empty second Ian); named people go 80 → 75/74; no duplicate face rows. Pascal (0.57) and PJ (0.50) stay apart and are logged; worth reviewing by hand. The same-person centroid median is 0.91; different named people have a p99.9 of 0.41, and only "Michelle Casanonve"/"Michelle Casanova" (a typo pair) reach 0.70. Around 10 ms per run once clean. Also fixed: merge_people left the merged-away person's rejections behind; they now move to the kept person.
Done in 6450f54, c78b798, 78df421 and ec7a8c0, released in v0.18.0; docs in b2f3936 (faces.md §19, catalog.md §8.4).
`dr_catalog::dedup_people::run` runs after every sync merge, on the sync worker, in one transaction:
- People with the same name (trimmed, case-folded) merge into the one with the most confirmed faces when every shared embedder's confirmed-face centroids agree at cosine ≥ 0.7, i.e. distance < 0.3 as requested. A namesake with no faces merges outright. Each side needs ≥ 2 confirmed faces to compare. A face confirmed as one and rejected as the other keeps them apart. Unnamed people are never touched.
- Face rows held twice (same image and embedder, IoU ≥ 0.5, cosine ≥ 0.7) fold into the stronger detector's face, with the assignment and rejections carried over.
- Propagation uses the existing `merged_into` redirect, which 0.17.0 peers already honour. Verified by a two-catalog test and on the real desktop/tablet pair, both converging on the same people.
On the reference library: Claudine, Jessie ×2, Mathias and Noemi merge (plus the tablet's empty second Ian); named people go 80 → 75/74; no duplicate face rows. Pascal (0.57) and PJ (0.50) stay apart and are logged; worth reviewing by hand. The same-person centroid median is 0.91; different named people have a p99.9 of 0.41, and only "Michelle Casanonve"/"Michelle Casanova" (a typo pair) reach 0.70. Around 10 ms per run once clean. Also fixed: `merge_people` left the merged-away person's rejections behind; they now move to the kept person.
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.
Seven names exist as two or more separate people on both devices. For example, Ian with 756 faces and Ian with 4, Jessie three times, PJ and Pascal. They were most likely typed separately on each device and then carried across by sync. There are also pairs of faces that are the same face held twice, where two detections of one face landed in the same photograph. There were none on the reference catalogs on 2026-09-26, but nothing prevents them.
Why
A person split in two shows half their photographs under each name, and every confirmation made on one half is invisible to the other. It is the "parallel libraries" failure again, this time at the level of people rather than faces.
Deliverable
A deduplication job that:
FaceDetector::outranks), and moves the confirmed assignment and any rejections onto the kept face;Acceptance
Done in
6450f54,c78b798,78df421andec7a8c0, released in v0.18.0; docs inb2f3936(faces.md §19, catalog.md §8.4).dr_catalog::dedup_people::runruns after every sync merge, on the sync worker, in one transaction:merged_intoredirect, which 0.17.0 peers already honour. Verified by a two-catalog test and on the real desktop/tablet pair, both converging on the same people.On the reference library: Claudine, Jessie ×2, Mathias and Noemi merge (plus the tablet's empty second Ian); named people go 80 → 75/74; no duplicate face rows. Pascal (0.57) and PJ (0.50) stay apart and are logged; worth reviewing by hand. The same-person centroid median is 0.91; different named people have a p99.9 of 0.41, and only "Michelle Casanonve"/"Michelle Casanova" (a typo pair) reach 0.70. Around 10 ms per run once clean. Also fixed:
merge_peopleleft the merged-away person's rejections behind; they now move to the kept person.