Hand shared address-book contacts back to their owners

FeatureInitializerService
Shipped
August 10, 2026 at 2:13 PM UTC
Author
Kamo
Commit
4bd974a

EmailService now scopes every contact read to contact_books.owner_id, which leaves the contacts already sitting in an ownerless organisation-default book unreachable. CONTACT.OWNER_ID is NOT NULL and has been populated on every row since the create path started setting it, so the member who added each contact is known exactly — nothing is inferred. This provisions a personal book per (organisation, owner) and moves each contact into its own owner's. Two things are deliberately stranded and counted in the log rather than forced: - UID collisions. uk_contact_uid_book is unique on (UID, CONTACT_BOOK_ID), so a contact whose UID already exists in the destination would fail the whole UPDATE and move nothing. Those rows stay put; rewriting a UID would break the CardDAV client that issued it. - Mixed-ownership groups. A contact_groups row has a book but no owner, so a group whose members belong to several people cannot be attributed to one of them. Only groups whose members share a single owner are moved. The ownerless book itself is kept: its check constraint is what makes it legal, and dropping it would cascade into groups and memberships. The stranded-count check tests contacts and groups, not just contacts. A group is attributed through its members' owners rather than through where those members live, so groups stay movable after every contact has already moved; counting contacts alone would skip them on a second run.

All changes

Like what you see shipping?

Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.

Start Free ForeverView Pricing