diff --git a/src/gmail-ai-classifier/code.gs b/src/gmail-ai-classifier/code.gs index f9965fd1..3631ccaf 100644 --- a/src/gmail-ai-classifier/code.gs +++ b/src/gmail-ai-classifier/code.gs @@ -1374,6 +1374,12 @@ const DOCUMENT_SIGNATURE_REQUEST_SNIPPET_TERMS_ = const TAX_AUTHORITY_SUBJECT_TERMS_ = 'notice from the alabama department of revenue|alabama department of revenue|my alabama taxes|department of revenue|state tax notice|irs notice|tax correspondence|notice of deficiency|notice of assessment|tax assessment|cp2000|cp504|1099-g notice' +const WEALTH_ADVISORY_SUBJECT_TERMS_ = + 'donor-advised|donor advised|fidelity charitable|appreciated shares|stock gift|in-kind stock|backdoor roth|roth ira|ira contribution|brokerage statement|holding report|gain/loss report|unrealized gain|tax-loss harvesting|personal strategy|financial strategy|wealth management' + +const WEALTH_ADVISORY_SNIPPET_TERMS_ = + 'donor-advised|donor advised|fidelity charitable|pershing|unrealized gain|appreciated shares|holding report|backdoor roth|roth ira|personal strategy|wealth management|financial advisor|shares to give|in-kind contribution' + const EVENT_INVITATION_TERMS_ = "you're invited|you are invited|invitation to|invitation:|invited you to|join us for|rsvp to|please rsvp|rsvp by|your invitation|has rsvp'd|has rsvpd|rsvpd|rsvp update|rsvp confirmation|will attend|attending|guest list update|responded to your invitation|new message on invitation|cannot attend|declined invitation|accepted invitation|view invitation|view your invitation" @@ -1890,6 +1896,50 @@ function deterministicRuleClassify_(sender, subject, snippet, thread, config) { ) } + // 02_Finance_Legal: Wealth Management & Investment Advisory Shield (Empower, Fidelity, Vanguard, Schwab, Pershing) + const isWealthMgmtSender = matchesSenderDomain_( + sender, + 'empower.com|fidelity.com|vanguard.com|schwab.com|pershing.com|morganstanley.com|merrilledge.com' + ) + const isWealthAdvisorySubject = containsAny_( + subLower, + WEALTH_ADVISORY_SUBJECT_TERMS_ + ) + const isWealthAdvisorySnippet = containsAny_( + snipLower, + WEALTH_ADVISORY_SNIPPET_TERMS_ + ) + const isWealthBroadcast = containsAny_( + subLower + ' ' + snipLower, + 'market recap|weekly market|daily market|market update|market insights|webinar|newsletter' + ) + + if ( + isWealthMgmtSender && + (isWealthAdvisorySubject || isWealthAdvisorySnippet) && + !isWealthBroadcast + ) { + const isDonationOrGift = containsAny_( + subLower + ' ' + snipLower, + 'charitable|donor-advised|donor advised|appreciated shares|stock gift' + ) + const subLabel = isDonationOrGift + ? 'Finance/Charitable-Donations' + : 'Finance/Banking' + return applyUserSentShield_( + makeDeterministicResult_( + '02_Finance_Legal', + subLabel, + 'Primary', + 'keep', + 1.0, + subject, + 'Direct wealth management or investment advisory communication preserved in Primary Inbox.', + 'Wealth Management Advisory Shield' + ) + ) + } + // 01_Household: Event Invitation & RSVP Shield (Evite, Paperless Post, Punchbowl, Partiful, Eventbrite) const isEventPlatformSender = matchesSenderDomain_( sender, diff --git a/src/gmail-ai-classifier/src/index.js b/src/gmail-ai-classifier/src/index.js index 2193e817..f4d3c513 100644 --- a/src/gmail-ai-classifier/src/index.js +++ b/src/gmail-ai-classifier/src/index.js @@ -1369,6 +1369,12 @@ const DOCUMENT_SIGNATURE_REQUEST_SNIPPET_TERMS_ = const TAX_AUTHORITY_SUBJECT_TERMS_ = 'notice from the alabama department of revenue|alabama department of revenue|my alabama taxes|department of revenue|state tax notice|irs notice|tax correspondence|notice of deficiency|notice of assessment|tax assessment|cp2000|cp504|1099-g notice' +const WEALTH_ADVISORY_SUBJECT_TERMS_ = + 'donor-advised|donor advised|fidelity charitable|appreciated shares|stock gift|in-kind stock|backdoor roth|roth ira|ira contribution|brokerage statement|holding report|gain/loss report|unrealized gain|tax-loss harvesting|personal strategy|financial strategy|wealth management' + +const WEALTH_ADVISORY_SNIPPET_TERMS_ = + 'donor-advised|donor advised|fidelity charitable|pershing|unrealized gain|appreciated shares|holding report|backdoor roth|roth ira|personal strategy|wealth management|financial advisor|shares to give|in-kind contribution' + const EVENT_INVITATION_TERMS_ = "you're invited|you are invited|invitation to|invitation:|invited you to|join us for|rsvp to|please rsvp|rsvp by|your invitation|has rsvp'd|has rsvpd|rsvpd|rsvp update|rsvp confirmation|will attend|attending|guest list update|responded to your invitation|new message on invitation|cannot attend|declined invitation|accepted invitation|view invitation|view your invitation" @@ -1885,6 +1891,50 @@ function deterministicRuleClassify_(sender, subject, snippet, thread, config) { ) } + // 02_Finance_Legal: Wealth Management & Investment Advisory Shield (Empower, Fidelity, Vanguard, Schwab, Pershing) + const isWealthMgmtSender = matchesSenderDomain_( + sender, + 'empower.com|fidelity.com|vanguard.com|schwab.com|pershing.com|morganstanley.com|merrilledge.com' + ) + const isWealthAdvisorySubject = containsAny_( + subLower, + WEALTH_ADVISORY_SUBJECT_TERMS_ + ) + const isWealthAdvisorySnippet = containsAny_( + snipLower, + WEALTH_ADVISORY_SNIPPET_TERMS_ + ) + const isWealthBroadcast = containsAny_( + subLower + ' ' + snipLower, + 'market recap|weekly market|daily market|market update|market insights|webinar|newsletter' + ) + + if ( + isWealthMgmtSender && + (isWealthAdvisorySubject || isWealthAdvisorySnippet) && + !isWealthBroadcast + ) { + const isDonationOrGift = containsAny_( + subLower + ' ' + snipLower, + 'charitable|donor-advised|donor advised|appreciated shares|stock gift' + ) + const subLabel = isDonationOrGift + ? 'Finance/Charitable-Donations' + : 'Finance/Banking' + return applyUserSentShield_( + makeDeterministicResult_( + '02_Finance_Legal', + subLabel, + 'Primary', + 'keep', + 1.0, + subject, + 'Direct wealth management or investment advisory communication preserved in Primary Inbox.', + 'Wealth Management Advisory Shield' + ) + ) + } + // 01_Household: Event Invitation & RSVP Shield (Evite, Paperless Post, Punchbowl, Partiful, Eventbrite) const isEventPlatformSender = matchesSenderDomain_( sender, diff --git a/src/gmail-ai-classifier/tests/code.test.js b/src/gmail-ai-classifier/tests/code.test.js index 30b6b96b..5733b3ea 100644 --- a/src/gmail-ai-classifier/tests/code.test.js +++ b/src/gmail-ai-classifier/tests/code.test.js @@ -4492,6 +4492,61 @@ describe('reclassifyThreadsByQuery Historical Realignment', () => { expect(res.action).toBe('keep') expect(res.confidence).toBe(1.0) }) + + test('classifies stock gift to donor-advised fund from wealth management into 02_Finance_Legal Finance/Charitable-Donations Primary keep', () => { + const res = mod.deterministicRuleClassify_( + 'grace.smith@empower.com', + 'Re: Donating appreciated shares to Fidelity Charitable', + 'Here is the unrealized gain/loss report and account statement for the stock gift to your donor advised fund.' + ) + expect(res).not.toBeNull() + expect(res.canonicalDomain).toBe('02_Finance_Legal') + expect(res.subLabel).toBe('Finance/Charitable-Donations') + expect(res.category).toBe('Primary') + expect(res.action).toBe('keep') + expect(res.confidence).toBe(1.0) + }) + + test('classifies backdoor roth / investment strategy from wealth management into 02_Finance_Legal Finance/Banking Primary keep', () => { + const res = mod.deterministicRuleClassify_( + 'shane.conway@empower.com', + 'RE: Backdoor Roth IRA?', + 'Attached is the personal strategy team overview regarding your annual Roth IRA contribution and investment strategy.' + ) + expect(res).not.toBeNull() + expect(res.canonicalDomain).toBe('02_Finance_Legal') + expect(res.subLabel).toBe('Finance/Banking') + expect(res.category).toBe('Primary') + expect(res.action).toBe('keep') + expect(res.confidence).toBe(1.0) + }) + + test('does not classify general market recap from wealth management into advisory shield', () => { + const res = mod.deterministicRuleClassify_( + 'news@empower.com', + 'Weekly Market Insights & Economic Outlook', + 'Join our quarterly webinar to discuss the market update and economic trends.' + ) + expect(res).toBeNull() + }) + + test('excludes broadcast subject even when advisory term is present', () => { + const res = mod.deterministicRuleClassify_( + 'news@empower.com', + 'Weekly Market Update', + 'Learn about the backdoor roth strategy in our webinar.' + ) + expect(res).toBeNull() + }) + + test('excludes broadcast snippet even when subject has no broadcast term', () => { + const res = mod.deterministicRuleClassify_( + 'news@empower.com', + 'Your wealth management invitation', + 'Join our wealth management webinar this week.' + ) + expect(res).toBeNull() + }) }) }) })