You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
P1-3 (forwarder_sent ledger enrichment, migration 059):
Enrich the worker-side send ledger with provider / provider_id /
recipient (masked) / template_kind / classification columns so
support staff can grep "what happened to email X" without log-
spelunking. New ledgerClaim struct + maskRecipientForLedger helper
thread these through every markSent call. Classification today is
'success' for confirmed 2xx and 'permanent_drop' for terminal-but-
no-2xx (F4 missing_renderer + provider Permanent / SkippedNoTemplate).
F4 (missing renderer reshape):
A kind registered in eventEmailBuilders but missing from
eventEmailBodyRenderers used to hold the cursor (queue pinned until
registry repaired). Now: log loud ERROR "missing_email_renderer",
increment metrics.EmailMissingRendererTotal{kind}, INSERT a
permanent_drop forwarder_sent row, and advance the cursor — the row
will never produce an email regardless of retries, so pinning the
queue is worse than dropping. The CI registry test
TestEventEmail_EverySupportedKindFullyWired (and the new
TestEventEmail_EveryBuilderHasARenderer inverse-coverage walk)
catches the half-registration at gate time; this is the runtime
backstop.
F5 (suppression vs send recipient):
Already extracted recipient once at the top of the loop in earlier
fix; this PR also threads that same recipient (masked) into the
forwarder_sent ledger row, so the suppression check, the provider
send, and the audit row all reference the SAME address.
Tests:
- Updated TestEventForwarder_MissingRenderer to assert the new
advance + permanent_drop ledger behavior (was: cursor held).
- Added TestEventEmail_EveryBuilderHasARenderer inverse-coverage
test (walks live eventEmailBuilders map, asserts each kind has a
renderer). Per CLAUDE.md rule 18 — iterate the live registry.
Coverage block — F4:
Symptom: missing_email_renderer (registered builder, missing renderer)
Enumeration: grep -n "eventEmailBuilders\[" worker/internal/jobs/event_email_mapping.go
+ iterate registry in TestEventEmail_EveryBuilderHasARenderer
Sites found: 1 fall-through path in Work()
Sites touched: 1 (event_email_forwarder.go Work() missing-renderer branch)
Coverage test: TestEventEmail_EveryBuilderHasARenderer
+ TestEventForwarder_MissingRenderer_LoudErrorDropAndAdvance
Live verified: awaiting deploy (sql migration 059 must land first via api)
Coverage block — F5:
Symptom: suppression check using row.OwnerEmail while send uses metadata.email
Enumeration: grep -n "row.OwnerEmail\|resolveRecipient" worker/internal/jobs/event_email_forwarder.go
Sites found: 1 (was: hasSuppression(row.OwnerEmail))
Sites touched: 1 (already fixed in earlier commit; this PR threads same
masked recipient into ledger.markSent for consistency)
Coverage test: TestEventForwarder_SuppressionUsesSentRecipient
Live verified: awaiting deploy
Coverage block — P1-3 ledger enrichment:
Symptom: forwarder_sent has audit_id + sent_at only — support can't trace
Enumeration: grep -rn "forwarder_sent\b" worker/ api/
Sites found: sqlSentLedger.markSent (1), sql/055_*.sql (1)
Sites touched: 4 (sqlSentLedger.markSent, ledgerClaim type, 3 call sites in
Work() — success / Permanent / F4 paths; migration 059 added)
Coverage test: TestEventForwarder_MissingRenderer_LoudErrorDropAndAdvance
(asserts ledger.lastClaim.Classification == permanent_drop +
Provider == "none" + ProviderID == "missing_renderer")
Live verified: awaiting deploy
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
"note", "kind has a builder but no Go renderer — holding cursor (NOT advancing). This is a registry bug; add a renderer to eventEmailBodyRenderers. Email NOT sent.",
724
+
"team_id", row.TeamID,
725
+
"note", "kind has a builder but no Go renderer — F4 permanent_drop. Add a renderer to eventEmailBodyRenderers. Email NOT sent; forwarder_sent ledger gets a permanent_drop row.",
0 commit comments