Attribute admin-sent emails to the sending person (invites, reminders, resends) + show From - #2089
Open
maebeale wants to merge 3 commits into
Open
Attribute admin-sent emails to the sending person (invites, reminders, resends) + show From#2089maebeale wants to merge 3 commits into
maebeale wants to merge 3 commits into
Conversation
maebeale
marked this pull request as ready for review
August 4, 2026 13:35
Invitation emails (both the single Invite button and the bulk console tool) were recorded with no sender, so the notifications UI showed them as "From: AWBW Portal" — anonymous. Attribute them to a real person. DeviseMailer now records Current.user as the notification sender. The Invite button already runs in a request where Current.user is set, so it gets this for free. The bulk path runs in the console with no request, so BulkInviteService takes a sender: and threads it through the job, which sets Current.user before sending. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Event reminders sent by hand from the bulk reminders page, and resent notifications, were created with no sender — so the communications index and show page labeled them "AWBW Portal" as if the portal sent them automatically. Pass the acting admin as the sender on both paths, and add a From row to the notification show page (person's name when a staff member sent it, "AWBW Portal" only for truly automated messages). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
maebeale
force-pushed
the
maebeale/bulk-invite-sender-attribution
branch
from
August 4, 2026 13:36
dd4f35c to
287f1d0
Compare
Threading the sender through the global Current.user meant a background job was mutating request-scoped state to talk to the mailer. It also silently un-gated AhoyTrackable's lifecycle tracking (which keys off Current.user): send_confirmation_instructions saves the record, so every invite pushed an event onto LifecycleBuffer, a thread-local that only ApplicationController ever flushes — in a job those piled up unflushed and undelivered. Devise sends with deliver_now, so the mailer holds the same User instance the caller does; the sender can just ride along on the record. The four admin-initiated call sites that were relying on ApplicationController setting Current.user now pass current_user explicitly, so they keep their attribution. Also collapses the "sender name, else AWBW Portal" fallback into NotificationDecorator#sender_name — the index was rendering a lowercase "AWBW portal" while the row partial and detail page said "AWBW Portal". Co-Authored-By: Claude <noreply@anthropic.com>
maebeale
commented
Aug 4, 2026
| # The staff member who triggered this confirmation email, when one did. Not | ||
| # persisted — DeviseMailer reads it off the record to attribute the | ||
| # notification it logs, since it has no request and no current_user. | ||
| attr_reader :confirmation_sender |
Collaborator
Author
There was a problem hiding this comment.
🤖 From Claude: This relies on Devise's default send_devise_notification using deliver_now, so DeviseMailer receives the same in-memory User the caller set the sender on. If anyone ever overrides it to deliver_later, the record round-trips through GlobalID and this ivar is silently lost — the notification would go back to reading "AWBW Portal".
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.
🤖 suggested review level: 3 Read 📖 sender attribution threaded through several admin-triggered send paths; one shared DeviseMailer/CreateNotification change + a show-page field
Why
What
Userrecord:send_confirmation_instructions(sender:)exposesconfirmation_sender, whichDeviseMailerreads when it logs the notification. Devise sends withdeliver_now, so the mailer holds the same instance the caller does.CreateNotificationacceptssender:.events#send_reminder) now passessender: current_userper recipient.notifications#resendattributes the resent copy to the admin who resent it.NotificationDecorator#sender_name(also used by the index and the notification row, which previously disagreed on capitalization).Tests
senderis the admin, and nosender_idis nil (guards the "AWBW Portal" regression).<dd>).sender_idthreaded to the job, and the job passes it intosend_confirmation_instructions.sender_namefor both branches.Notes
Current.user: a background job shouldn't mutate request-scoped state, andCurrent.useris what gatesAhoyTrackable#track_lifecycle_event— setting it in a job made every invite push an event ontoLifecycleBuffer, a thread-local onlyApplicationControllerflushes. Those piled up unflushed and undelivered.users#send_welcome_instructions,ProcessEmailChange,ProcessEmailManualConfirm,ProcessConfirmation) pass the sender explicitly; the last four sit right next to an existingupdated_by = @current_user"credit the acting admin" line.sender:on the console bulk invite is optional — omitting it falls back to "AWBW Portal" and logs a warning.auth.confirmation_email_sentahoy event is attributed to the sender too, when there is one.