From 4ad803aa8cf9561ac53cdddfda7539b5cd62dadd Mon Sep 17 00:00:00 2001 From: Sarthak Agrawal Date: Mon, 10 Aug 2026 04:26:45 +0530 Subject: [PATCH 1/3] perf: optimize weekly digest aggregation --- .gitignore | 2 +- src/lib/__tests__/digest-performance.test.ts | 61 +++++++++++ src/lib/digest.ts | 106 ++++++++++++------- 3 files changed, 127 insertions(+), 42 deletions(-) create mode 100644 src/lib/__tests__/digest-performance.test.ts diff --git a/.gitignore b/.gitignore index 038d7bd..c751c7d 100644 --- a/.gitignore +++ b/.gitignore @@ -31,8 +31,8 @@ apps/docs-blume/.astro/ # Local agent logs / scratch .agent-logs/ .devin-logs/ +.codevetter/ *.agent-log # Foundry evidence — generated by `pnpm foundry:evidence` and CI; not source. foundry-evidence.json - diff --git a/src/lib/__tests__/digest-performance.test.ts b/src/lib/__tests__/digest-performance.test.ts new file mode 100644 index 0000000..08f9366 --- /dev/null +++ b/src/lib/__tests__/digest-performance.test.ts @@ -0,0 +1,61 @@ +import { createHash } from 'node:crypto'; +import { performance } from 'node:perf_hooks'; +import { expect, test } from 'vitest'; + +import { buildWeeklyDigest, type DigestEmailInput } from '../digest'; + +const SIZES = [1000, 10_000, 50_000]; +const ITERATIONS = 25; +const NOW = new Date('2026-08-10T00:00:00Z'); +const EXPECTED_DIGESTS = new Map([ + [1000, '3d96492d0c31b305026a3d479f136ad1315531f4411287021336b9e9b38c8287'], + [10_000, 'dfd88058f6f0a532fc03780d33d34242aded8592f242e1a9a82aa1fa6dc83cc9'], + [50_000, 'a613acd0d4419028f3d0306480318ba3cb4d90212e19dfce5af050cd3274d8a6'], +]); + +test('weekly digest scales across local inbox sizes', { timeout: 30_000 }, () => { + const largestInbox = buildInbox(SIZES.at(-1) ?? 0); + const metrics: string[] = []; + + for (const size of SIZES) { + const emails = largestInbox.slice(0, size); + const expected = JSON.stringify(buildWeeklyDigest(emails, { now: NOW })); + expect(createHash('sha256').update(expected).digest('hex')).toBe(EXPECTED_DIGESTS.get(size)); + let durationMs = 0; + for (let iteration = 0; iteration < ITERATIONS; iteration += 1) { + const startedAt = performance.now(); + const digest = buildWeeklyDigest(emails, { now: NOW }); + durationMs += performance.now() - startedAt; + expect(JSON.stringify(digest)).toBe(expected); + } + metrics.push(`size${size}=${(durationMs / ITERATIONS).toFixed(3)}ms/op`); + } + + console.log(`[benchmark] ${metrics.join(' ')} (${ITERATIONS} iterations)`); + console.log(`[resource] largest_input_rows=${largestInbox.length}`); +}); + +function buildInbox(size: number): DigestEmailInput[] { + const subjects = [ + 'Invoice for consulting work', + 'Meeting invite for project review', + 'Interview follow-up', + 'Family birthday plans', + 'Order tracking update', + 'General project note', + ]; + return Array.from({ length: size }, (_, index) => { + const dayOffset = (index * 17) % 180; + const date = new Date(NOW); + date.setUTCDate(date.getUTCDate() - dayOffset); + return { + id: `email-${index}`, + threadId: `thread-${index % 500}`, + subject: subjects[index % subjects.length], + from: `Sender ${index % 200} `, + date: date.toISOString(), + snippet: `Message ${index}`, + labelIds: index % 17 === 0 ? ['STARRED'] : [], + }; + }); +} diff --git a/src/lib/digest.ts b/src/lib/digest.ts index cc90354..d0dc6e2 100644 --- a/src/lib/digest.ts +++ b/src/lib/digest.ts @@ -110,50 +110,79 @@ export function buildWeeklyDigest( const weekStartMs = periodStart.getTime(); const weekEndMs = periodEnd.getTime() + 7 * 24 * 60 * 60 * 1000; + const quietCutoffMs = quietCutoff.getTime(); + const staleCutoffMs = staleCutoff.getTime(); + const nowMs = now.getTime(); - const senderMap = new Map(); + const senderMap = new Map(); const threadMap = new Map< string, { subject: string; - dates: number[]; + lastAt: number; + messageCount: number; starred: boolean; } >(); const themeDomainCounts = new Map>(); + const senderCache = new Map< + string, + { email: string; displayName: string; newsletter: boolean; domain: string } + >(); + const themeKeyCache = new Map(); for (const email of emails) { - const { email: senderEmail, displayName } = parseSender(email.from); - if (isNewsletterSender(senderEmail, displayName)) continue; + let senderDetails = senderCache.get(email.from); + if (!senderDetails) { + const parsed = parseSender(email.from); + senderDetails = { + ...parsed, + newsletter: isNewsletterSender(parsed.email, parsed.displayName), + domain: domainFromEmail(parsed.email), + }; + senderCache.set(email.from, senderDetails); + } + if (senderDetails.newsletter) continue; - const t = new Date(email.date).getTime(); + const t = Date.parse(email.date); if (Number.isNaN(t)) continue; - const sender = senderMap.get(senderEmail) ?? { - displayName, - dates: [], - count: 0, - }; - sender.dates.push(t); - sender.count += 1; - senderMap.set(senderEmail, sender); - - const thread = threadMap.get(email.threadId) ?? { - subject: email.subject, - dates: [], - starred: false, - }; - thread.dates.push(t); - if (email.labelIds?.includes('STARRED')) thread.starred = true; - threadMap.set(email.threadId, thread); + const sender = senderMap.get(senderDetails.email); + if (sender) { + if (t > sender.lastAt) sender.lastAt = t; + sender.count += 1; + } else { + senderMap.set(senderDetails.email, { + displayName: senderDetails.displayName, + lastAt: t, + count: 1, + }); + } + + const thread = threadMap.get(email.threadId); + if (thread) { + if (t > thread.lastAt) thread.lastAt = t; + thread.messageCount += 1; + if (email.labelIds?.includes('STARRED')) thread.starred = true; + } else { + threadMap.set(email.threadId, { + subject: email.subject, + lastAt: t, + messageCount: 1, + starred: email.labelIds?.includes('STARRED') ?? false, + }); + } if (t >= weekStartMs && t < weekEndMs) { - const domain = domainFromEmail(senderEmail); - const themeKey = themeKeyFromSubject(email.subject); + let themeKey = themeKeyCache.get(email.subject); + if (!themeKey) { + themeKey = themeKeyFromSubject(email.subject); + themeKeyCache.set(email.subject, themeKey); + } const domainMap = themeDomainCounts.get(themeKey) ?? new Map(); - domainMap.set(domain, (domainMap.get(domain) ?? 0) + 1); + domainMap.set(senderDetails.domain, (domainMap.get(senderDetails.domain) ?? 0) + 1); themeDomainCounts.set(themeKey, domainMap); } } @@ -161,14 +190,12 @@ export function buildWeeklyDigest( const relationshipsQuiet: RelationshipQuiet[] = []; for (const [senderEmail, data] of senderMap) { if (data.count < minPriorMessages) continue; - const sorted = [...data.dates].sort((a, b) => b - a); - const lastAt = sorted[0]; - if (lastAt >= quietCutoff.getTime()) continue; - const quietDays = Math.floor((now.getTime() - lastAt) / (24 * 60 * 60 * 1000)); + if (data.lastAt >= quietCutoffMs) continue; + const quietDays = Math.floor((nowMs - data.lastAt) / (24 * 60 * 60 * 1000)); relationshipsQuiet.push({ senderEmail, displayName: data.displayName, - lastMessageAt: new Date(lastAt).toISOString(), + lastMessageAt: new Date(data.lastAt).toISOString(), priorMessageCount: data.count, quietDays, reason: 'no_messages_in_quiet_window', @@ -178,34 +205,31 @@ export function buildWeeklyDigest( const threadsToRevisit: ThreadRevisit[] = []; for (const [threadId, data] of threadMap) { - const sorted = [...data.dates].sort((a, b) => b - a); - const lastAt = sorted[0]; - const messageCount = sorted.length; - if (lastAt >= staleCutoff.getTime()) continue; + if (data.lastAt >= staleCutoffMs) continue; if (data.starred) { threadsToRevisit.push({ threadId, subject: data.subject, - lastMessageAt: new Date(lastAt).toISOString(), + lastMessageAt: new Date(data.lastAt).toISOString(), reason: 'starred_stale', - messageCount, + messageCount: data.messageCount, }); continue; } - if (messageCount >= 4) { + if (data.messageCount >= 4) { threadsToRevisit.push({ threadId, subject: data.subject, - lastMessageAt: new Date(lastAt).toISOString(), + lastMessageAt: new Date(data.lastAt).toISOString(), reason: 'long_thread_stale', - messageCount, + messageCount: data.messageCount, }); } } - threadsToRevisit.sort( - (a, b) => new Date(a.lastMessageAt).getTime() - new Date(b.lastMessageAt).getTime() + threadsToRevisit.sort((a, b) => + a.lastMessageAt < b.lastMessageAt ? -1 : a.lastMessageAt > b.lastMessageAt ? 1 : 0 ); const weeklyThemes: WeeklyTheme[] = [...themeDomainCounts.entries()] From 47298bca7069b4fb9cfa9b1173d9af784e4e1a5a Mon Sep 17 00:00:00 2001 From: Sarthak Agrawal Date: Wed, 12 Aug 2026 18:23:37 +0530 Subject: [PATCH 2/3] perf: cache weekly digest dates --- src/lib/digest.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/lib/digest.ts b/src/lib/digest.ts index d0dc6e2..ea2b303 100644 --- a/src/lib/digest.ts +++ b/src/lib/digest.ts @@ -131,6 +131,7 @@ export function buildWeeklyDigest( string, { email: string; displayName: string; newsletter: boolean; domain: string } >(); + const dateCache = new Map(); const themeKeyCache = new Map(); for (const email of emails) { @@ -146,7 +147,11 @@ export function buildWeeklyDigest( } if (senderDetails.newsletter) continue; - const t = Date.parse(email.date); + let t = dateCache.get(email.date); + if (t === undefined) { + t = Date.parse(email.date); + dateCache.set(email.date, t); + } if (Number.isNaN(t)) continue; const sender = senderMap.get(senderDetails.email); From 7e6a94e4913ba3789a8b0ed7727673cbb8eb5338 Mon Sep 17 00:00:00 2001 From: Sarthak Agrawal Date: Wed, 12 Aug 2026 18:32:51 +0530 Subject: [PATCH 3/3] fix: keep digest date caching within complexity budget --- src/lib/digest.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/lib/digest.ts b/src/lib/digest.ts index ea2b303..f3c5c0c 100644 --- a/src/lib/digest.ts +++ b/src/lib/digest.ts @@ -86,6 +86,15 @@ function isNewsletterSender(email: string, displayName: string): boolean { return NEWSLETTER_RE.test(email) || NEWSLETTER_RE.test(displayName); } +function getCachedTimestamp(value: string, cache: Map): number { + const cached = cache.get(value); + if (cached !== undefined) return cached; + + const parsed = Date.parse(value); + cache.set(value, parsed); + return parsed; +} + /** * Build a deterministic weekly digest from locally cached emails. */ @@ -147,11 +156,7 @@ export function buildWeeklyDigest( } if (senderDetails.newsletter) continue; - let t = dateCache.get(email.date); - if (t === undefined) { - t = Date.parse(email.date); - dateCache.set(email.date, t); - } + const t = getCachedTimestamp(email.date, dateCache); if (Number.isNaN(t)) continue; const sender = senderMap.get(senderDetails.email);