Let the directory learn the speakers your events already have - #329
Closed
EllAchE wants to merge 3 commits into
Closed
Let the directory learn the speakers your events already have#329EllAchE wants to merge 3 commits into
EllAchE wants to merge 3 commits into
Conversation
…' into crm-adopt-event-speakers-20260817t2100
/crm says it holds "every speaker and contact your organization has worked with, across all events". It held none of them. The only bridge between the two stores pushed org -> event, through the contact record's "Add to an event" button, so an organizer who imported a roster and then opened the CRM found "Your speaker database is empty" and was asked to import the same CSV a second time. The directory now adopts the speakers on every event the organizer runs, on read. Adoption is keyed by email and idempotent: a speaker already in the directory is linked to the event rather than duplicated, one already linked is left alone, and a person on two events is one record with two links. It only ever adds. A contact the organizer has since renamed, re-companied or written a bio for is theirs, and a roster row is not entitled to overwrite it. Adopted records carry `source: 'event roster'` so they are distinguishable from ones typed in by hand. Both entrances do it — the directory and the dashboard — because landing on either first should not show a database of nobody. The lookup includes merged-away tombstones. `contact_owner_email` is unique across them, so a merged row still holds its address and inserting over it would fail rather than adopt. This closes the enrolment half of the finding. Edits still do not flow back: renaming someone on an event speaker record does not update their directory contact, and which store wins on a conflict is a product decision this change does not make. Found by `sbek` CRM-S1 against run 2026-08-17T05-46-05.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #328 (
event-person-names-20260817t2030) — it useseventPersonNamefrom that branch. GitHub retargets this tomainwhen #328 merges.sbekCRM-S1, run2026-08-17T05-46-05(major):The fix
The directory adopts the speakers on every event the organizer runs, on read. Keyed by email, idempotent:
planRosterAdoptionis the pure half and holds all of that;adoptEventSpeakersdoes the reads and writes around it.Adoption only ever adds. A contact the organizer has since renamed, re-companied or written a bio for is theirs, and a roster row is not entitled to overwrite it. Adopted records carry
source: 'event roster'so they are distinguishable from ones typed in by hand.Both entrances run it — the directory and the dashboard — because landing on either first should not show a database of nobody.
The contact lookup includes merged-away tombstones on purpose:
contact_owner_emailis unique across them, so a merged row still holds its address and inserting over it would fail rather than adopt.What this does not do
The enrolment half of the finding is closed. The edit half is not: renaming someone on an event speaker record still does not update their directory contact. Which store wins on a conflicting edit is a product decision, and picking one silently inside a bug fix would be the wrong place to make it.
Cost
One extra read on the two CRM entry points, and writes only on the first visit after a roster changes — after that the plan comes back empty and nothing is written.
Tests
lib/services/crm.test.tsgains 7 cases onplanRosterAdoption: adopts an unknown speaker and records the event; does nothing the second time; links rather than duplicates someone already in the directory; one record and two links for a speaker on two events; adds only the missing event for a partially-linked speaker; keys the address case-insensitively (the column is stored lowercased, so a case-sensitive key would adopt twice); skips a participant with no address rather than guessing.lint,typecheck,test(2051 in 190 files),build,docs:openapi,docs:mcpall clean.Not merged and not deployed.