Create ParticipantEvent when someone is invited - #149
Merged
leowilkin merged 3 commits intoSep 26, 2026
Merged
Conversation
An Invitation is keyed by email, not by registration, so deleting a ParticipantEvent left the invitation behind as a live key. The onboarding controller rebuilds a registration from a surviving invitation whenever it finds none for the signed-in person, so the original emailed link simply re-registered them as in_progress. The token route at least expires, but the no-token route (event selected from the dashboard) matches on `expires_at > now OR accepted_at IS NOT NULL`, and accepting happens on the first click of the invite link. An invitation anyone has ever opened therefore never stopped working: an invitation expired six months earlier and accepted a year earlier still re-created the registration. Deliberately not a destroy callback. ParticipantMergeService destroys a registration to replace it with a more progressed duplicate, and an event teardown destroys them wholesale; neither means "uninvite this person". It is called from the two places that do mean it — the admin and API destroy actions — each inside a transaction with the destroy. Withdrawal is untouched: it stays reversible, and redirect_if_withdrawn already catches a withdrawn person returning by their link. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WNAizmNs7QmABqaVXa6Boy
The mail job was enqueued inside the transaction. When registering the invitee failed validation, the invitation row rolled back but the job still ran, and the mailer's find-or-create resurrected the invitation and emailed a link with no registration behind it. Invitation.issue! now delegates sending to a new deliver_later, which the API calls once both rows are committed. The duplicate-registration guard now matches email case-insensitively, like the lookup that attaches the registration, and that lookup picks the oldest row when an address has duplicate participant records.
leowilkin
approved these changes
Sep 26, 2026
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.
Currently, a ParticipantEvent is not created when a participant is invited via the API. However, CSV imports do create a participant.
This PR unifies the two so that a ParticipantEvent is created when an invitation is created via the API. It also deletes all invitations for the email when a ParticipantEvent is deleted, so that deleting a ParticipantEvent also revokes their invitation.