Review: Refactor email sending to use ActionMailer with Gmail API delivery adapter
Verdict: APPROVED
Re-review of board item #1598 after scope refinement. Previous review
review-1598-2026-06-24 returned NEEDS_REFINEMENT with 6 recommendations. 4 of 5 [BODY] recommendations fully addressed; 1 [SCOPE] item remains but is non-blocking.Previous Recommendations Status
- [x] [BODY] Fix Repo field -- FIXED. Now correctly says
ldraney/westside-basketball. - [x] [BODY] Add Feature Flag section -- FIXED. Section present with "none" and rationale ("internal refactor, same external behavior").
- [x] [BODY] Rewrite File Targets -- FIXED. Paths align with spike #47 deliverables. Correctly references
lib/delivery_methods/gmail_api.rb(namespaced),app/mailers/parent_mailer.rb, marks spike-delivered files as NOT to touch. Includesoauth_tokensmigration as "potentially add." - [x] [BODY] Rewrite Acceptance Criteria -- FIXED. ACs reference ParentMailer, EmailLogging, deliver_later. Nonexistent TournamentEmailService removed. Token storage AC added.
- [~] [BODY] Clarify blast/single-send routes -- PARTIALLY FIXED. See minor findings below.
- [ ] [SCOPE] Create arch note
arch-westside-basketball-- NOT ADDRESSED. Non-blocking; tracked as standing recommendation.
Template Completeness
- [x] Type -- Feature
- [x] Lineage -- Follows Spike #47 / PR #52
- [x] Repo --
ldraney/westside-basketball(correct) - [x] User Story -- Present, well-scoped
- [x] Context -- Excellent. Explains spike deliverables, what's wired vs not, production concern.
- [x] File Targets -- Present, accurate (see verification below)
- [x] Feature Flag -- "none" with rationale
- [x] Acceptance Criteria -- 8 items, testable
- [x] Test Expectations -- Present with run command
- [x] Constraints -- Present (SolidQueue, sanitize raw body, no UI breakage)
- [x] Checklist -- Present
- [x] Related -- Present
Traceability
- [x] story:WS-S7 label -- "As an admin, I want to send branded email announcements so that parent comms are professional and consistent" -- verified in project-westside-basketball user-stories section
- [~] arch:westside-basketball label -- label present but no backing architecture note
arch-westside-basketballexists in pal-e-docs. Standing [SCOPE] recommendation from previous review. Non-blocking for this ticket. - [x] Forgejo issue -- ldraney/westside-basketball#14, state: open
File Targets
All file targets verified against main branch and spike branch (
origin/47-actionmailer-spike).Files to modify (verified):
- [x]
config/environments/development.rb-- exists on main. Nodelivery_methodset currently. Correct target. - [x]
config/environments/production.rb-- exists on main. Nodelivery_methodset currently. Correct target. - [x]
app/controllers/emails_controller.rb-- exists on main. Confirmed:send_emailaction callsGmailClient.new/client.send_emaildirectly (lines 28-29). Spike did NOT modify this file. Correct target. - [x]
app/mailers/parent_mailer.rb-- exists on spike branch. Confirmed: spike's version does NOT includeEmailLogging. Adding the concern here is correct. - [x]
app/mailers/application_mailer.rb-- exists on main, updated by spike. Issue correctly says "no changes expected."
Files potentially added (verified):
- [x]
db/migrate/*_move_oauth_tokens_to_db.rb--oauth_tokenstable already exists in schema withprovider,account,token_datacolumns.OauthTokenmodel exists. Migration would updateDeliveryMethods::GmailApito read from DB instead ofconfig/gmail/token.json. - [x]
lib/delivery_methods/gmail_api.rb-- exists on spike branch. Currently reads file-based tokens. Modification for DB tokens is correctly scoped.
Files NOT to touch (verified):
- [x]
app/mailers/concerns/email_logging.rb-- exists on spike branch. Complete implementation withafter_delivercallback. No changes needed. - [x]
app/views/parent_mailer/-- 4 templates exist on spike branch (announcement + payment_reminder, HTML + text). No changes needed.
Repo Placement
Correct. Issue filed on
ldraney/westside-basketball, Repo section says ldraney/westside-basketball. All file targets are within this repo. Single-repo scope.Dependencies
- depends:47 (Spike: Rails email sending) -- label present and correct. Spike is board item #1579, column
in_progress, PR #52 open. This issue CANNOT start until spike merges. Clearly documented in issue Lineage and Context. - Board item #1580 (Queens EYBL email,
todo) and #1581 (Email #2 TBD,backlog) both carrydepends:47. After #47 merges and #14 activates the mechanism, those items become unblocked. - SolidQueue dependency noted in Constraints section --
solid_queuegem confirmed in Gemfile. Queue adapter activation for production may need attention (currently commented out in production.rb).
Acceptance Criteria
8 criteria. Assessment:
- [x] AC 1 (delivery_method in dev/prod) -- Testable. Grep config files.
- [x] AC 2 (announcement uses ParentMailer.deliver_later) -- Testable. Unit test on controller.
- [x] AC 3 (payment_reminder uses ParentMailer.deliver_later) -- Testable, but note:
EmailsControllercurrently has no payment_reminder action. Agent will need to add one or clarify scope. Minor ambiguity -- acceptable, agent can resolve. - [x] AC 4 (EmailLogging in ParentMailer) -- Testable. Check concern inclusion and email_log writes.
- [~] AC 5 (compose form works e2e) -- Route is
/emails/compose, NOT/admin/emails/newas stated. Minor path error but intent is clear. Agent will find the correct route. - [x] AC 6 (blast functionality works) -- Current "blast" is the compose form sending to selected team parents. No separate blast endpoint. Intent is clear.
- [x] AC 7 (GmailClient deprecated) -- Testable. Remove send calls, keep read/inbox methods.
- [x] AC 8 (token storage) -- "Migrated to DB or documented as follow-up" -- flexible, testable either way.
Test run command:
bin/rails test test/controllers/emails_controller_test.rb test/mailers/ -- these files do not exist yet. Agent will create them. This is expected for a feature ticket.Blast Radius
Low.
GmailClient is used in exactly 2 places: emails_controller.rb lines 28-29 (send_email) and line 37 (inbox). The send path gets replaced by ParentMailer; the inbox/read path stays on GmailClient. No other consumers. No cross-repo impact.If token storage migration is included, blast radius increases slightly to
lib/delivery_methods/gmail_api.rb and app/services/gmail_client.rb (both read tokens). Contained within the same repo.Decomposition Assessment
5 files to modify across 1 repo, 8 acceptance criteria. Post-spike scope is primarily: activate delivery method in 2 config files, rewrite 1 controller action, add 1 concern include, optionally migrate token storage. Estimated agent work: ~5 minutes. Borderline on AC count (8 > 5 threshold) but the individual changes are small and tightly coupled. No decomposition needed.
Recommendations
Minor items -- none blocking. Verdict is APPROVED.
- [BODY] AC 5: fix route path
/admin/emails/newto/emails/compose(routes.rb confirms emails are not under admin namespace). - [BODY] AC 3: clarify whether #14 adds a
payment_remindercontroller action or if that's future work (current controller only has genericsend_email). - [SCOPE] Create architecture note
arch-westside-basketballin pal-e-docs (standing from previous review, non-blocking).
If APPROVED: No action needed. Minor body items can be addressed during implementation.