Say how synced faces and same-name people merge since #77 and #78

catalog.md §8.2 still said confirmed and rejected faces were matched to
local faces "by box", and that the merge reads only a remote face's box
and model; faces.md said `match_faces` "still matches by overlap alone
across devices". Since #77 the match falls back to embeddings, on the
photographs where a box leaves a remote face over, and since #78
`dedup_people` folds people of one name whose faces agree after every
sync. Both now say so, with the thresholds and the reason a less
decisive pair stays unmatched, taken from the code's own documentation.
This commit is contained in:
2026-09-26 18:30:07 -04:00
parent ec7a8c07ee
commit b2f3936a53
2 changed files with 42 additions and 4 deletions
+8 -2
View File
@@ -723,7 +723,12 @@ merges reuses those rules or keys on the same identities, and each has no other
- **Collections and their membership** — by uuid and revision, membership as a set union.
- **Keywords** — the vocabulary by the same verdict, the assignments as a union.
- **People and identity judgements** — people by uuid and revision, and the confirmed and rejected
face assignments matched to local faces by box (`merge::match_faces`).
face assignments matched to local faces (`merge::match_faces`): by box first, and — since 0.18.0,
only on the photographs where a remote face is left over — by embedding, a pair being accepted
at cosine ≥ 0.7 when each is the other's best by a lead of ≥ 0.2 (#77; [faces.md §18.2](faces.md)). After every merge,
`dedup_people` folds people of one name whose confirmed faces agree, and a face held twice in
one photograph, through the ordinary `merged_into` redirect, which older builds already honour
(#78; [faces.md §19](faces.md)).
- **Albums** (FR-EXP-10, 0.17.0) — by uuid and revision with tombstones, and what went into each as
a set union keyed on the server's file id (a content hash on a folder library). An album's
server folder is a column of its row and travels with it; a folder on *this device* is in
@@ -754,7 +759,7 @@ it goes anywhere.
**The face crops stay out of the upload.** A crop is a ~5 KB JPEG on each `faces` row. On a 19k-face
library they are 96 MB of a 158 MB catalog. The face shards carry them to other devices, once each.
The merge reads a remote face's box and model to match it to a local one, never its pixels. No
The merge reads a remote face's box and model to match it to a local one — and, where the boxes cannot decide, its embedding — never its pixels. No
device adopts a downloaded catalog as its own: a fresh device starts empty and takes faces, crops
included, from the shards. So the snapshot's `crop` is NULL, and a merge never writes a local
crop. They were first stripped (2026-08) by copying the whole file with the backup API, setting
@@ -778,6 +783,7 @@ integer ids stay local and are never compared across catalogs.
| Deletion | Tombstone (`deleted = 1`) carrying a revision | Without it, merging against a device that still holds the collection resurrects it. With a revision, deletion competes on equal footing with a rename |
| An image the remote has and we do not | Skip the membership row | It joins on a later merge, once a scan has catalogued the file. Not an error |
| A remote from a newer schema | Decline before attaching | Attempting it would fail mid-transaction rather than declining cleanly |
| People with the same name | Folded after each merge when their faces agree ([faces.md §19](faces.md)) | Names typed separately on two devices otherwise stay two people for ever |
Merging is idempotent: running it twice reports no changes the second time. That property is tested,
because a merge that oscillates would upload on every sync forever.