Learned patterns from labels ( cold emails only )#689
Conversation
|
@edulelis is attempting to deploy a commit to the Inbox Zero OSS Program Team on Vercel. A member of the Team first needs to authorize it. |
WalkthroughAdds typed history events and label-removed handling: history fetch includes labelRemoved, events are wrapped with a type and dispatched to processHistoryItem which delegates LABEL_REMOVED to a new handleLabelRemovedEvent that resolves label names, upserts cold-email rejections, and can save learned exclusion patterns; tests and mocks updated. Changes
Sequence Diagram(s)sequenceDiagram
participant Gmail
participant ProcHistory as process-history
participant ProcItem as processHistoryItem
participant LabelHandler as handleLabelRemovedEvent
participant Labels as GmailLabels
participant Provider as EmailProvider
participant DB as Prisma
participant Learn as saveLearnedPatterns
Gmail->>ProcHistory: deliver history (messageAdded/labelAdded/labelRemoved)
ProcHistory->>ProcHistory: build allEvents (typed)
loop each event
ProcHistory->>ProcItem: processHistoryItem({type, item}, ctx)
alt type == LABEL_REMOVED
ProcItem->>Provider: ensure provider
ProcItem->>LabelHandler: handleLabelRemovedEvent(item, {gmail,emailAccount,provider})
LabelHandler->>Provider: provider.getMessage(messageId)
LabelHandler->>Labels: list/getLabelById -> resolve label names
alt label == cold_email
LabelHandler->>DB: upsert ColdEmail(status=USER_REJECTED_COLD)
else label maps and executed rule matches
LabelHandler->>Learn: saveLearnedPatterns(exclusion from sender)
else
LabelHandler-->>ProcItem: no-op / skip learning
end
else
ProcItem->>Provider: provider.getMessage(...) and continue existing flow
end
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~40 minutes Possibly related PRs
Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
Status, Documentation and Community
|
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (4)
apps/web/app/api/google/webhook/process-history.ts (1)
240-251: Consider performance impact of double filtering and memory usageThe current implementation creates multiple intermediate arrays which could impact performance for large history sets. Consider combining the filtering logic to reduce memory allocation and iterations.
- // For label events, we want to process them regardless of inbox status - const labelEvents = historyMessages.filter( - (m) => h.labelsAdded?.includes(m) || h.labelsRemoved?.includes(m), - ); - - // For message events, we filter to only inbox/sent messages - const messageEvents = historyMessages - .filter((m) => h.messagesAdded?.includes(m)) - .filter(isInboxOrSentMessage); - - const allEvents = [...labelEvents, ...messageEvents]; - const uniqueMessages = uniqBy(allEvents, (m) => m.message?.id); + // Process events based on type with appropriate filtering + const eventsToProcess = historyMessages.filter((m) => { + // Label events are processed regardless of inbox status + if (h.labelsAdded?.includes(m) || h.labelsRemoved?.includes(m)) { + return true; + } + // Message events are filtered to only inbox/sent messages + if (h.messagesAdded?.includes(m)) { + return isInboxOrSentMessage(m); + } + return false; + }); + + const uniqueMessages = uniqBy(eventsToProcess, (m) => m.message?.id);apps/web/app/api/google/webhook/process-history-item.ts (3)
65-72: Consider more explicit type checking for label eventsThe current check
"labelIds" in itemcould be fragile. Consider using a more explicit type guard or checking for the specific event type.- if ("labelIds" in item && item.labelIds && item.labelIds.length > 0) { + // Check if this is a label removal event (has labelIds but not a message addition) + const isLabelRemovalEvent = "labelIds" in item && + item.labelIds && + item.labelIds.length > 0 && + !("messagesAdded" in (item as any)); + + if (isLabelRemovalEvent) {
345-352: Consider handling potential failures in parallel label resolutionWhile using
Promise.allfor parallel label resolution is efficient, consider usingPromise.allSettledto handle individual label resolution failures gracefully.- const removedLabels = await Promise.all( - removedLabelIds.map((labelId: string) => - getLabelById({ gmail, id: labelId }), - ), - ); - const removedLabelNames = removedLabels - .map((label: gmail_v1.Schema$Label) => label?.name) - .filter((label: string | null | undefined): label is string => !!label); + const labelResults = await Promise.allSettled( + removedLabelIds.map((labelId: string) => + getLabelById({ gmail, id: labelId }), + ), + ); + const removedLabelNames = labelResults + .filter((result): result is PromiseFulfilledResult<gmail_v1.Schema$Label> => + result.status === 'fulfilled' && !!result.value?.name) + .map((result) => result.value.name as string); + + // Log any failed label resolutions + const failedLabels = labelResults.filter(r => r.status === 'rejected'); + if (failedLabels.length > 0) { + logger.warn("Failed to resolve some label IDs", { + failedCount: failedLabels.length, + ...loggerOptions + }); + }
462-466: Consider implementing TO_REPLY rule learning in the futureThe TODO comment indicates missing functionality. Consider tracking this as a GitHub issue for future implementation.
Would you like me to create a GitHub issue to track the implementation of learning from TO_REPLY rules?
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
apps/web/app/api/google/webhook/process-history-item.test.ts(4 hunks)apps/web/app/api/google/webhook/process-history-item.ts(9 hunks)apps/web/app/api/google/webhook/process-history.ts(2 hunks)
🧰 Additional context used
📓 Path-based instructions (11)
apps/web/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use@/for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX
Files:
apps/web/app/api/google/webhook/process-history-item.tsapps/web/app/api/google/webhook/process-history.tsapps/web/app/api/google/webhook/process-history-item.test.ts
apps/web/app/**
📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)
NextJS app router structure with (app) directory
Files:
apps/web/app/api/google/webhook/process-history-item.tsapps/web/app/api/google/webhook/process-history.tsapps/web/app/api/google/webhook/process-history-item.test.ts
!{.cursor/rules/*.mdc}
📄 CodeRabbit Inference Engine (.cursor/rules/cursor-rules.mdc)
Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location
Files:
apps/web/app/api/google/webhook/process-history-item.tsapps/web/app/api/google/webhook/process-history.tsapps/web/app/api/google/webhook/process-history-item.test.ts
**/*.ts
📄 CodeRabbit Inference Engine (.cursor/rules/form-handling.mdc)
**/*.ts: The same validation should be done in the server action too
Define validation schemas using Zod
Files:
apps/web/app/api/google/webhook/process-history-item.tsapps/web/app/api/google/webhook/process-history.tsapps/web/app/api/google/webhook/process-history-item.test.ts
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/logging.mdc)
**/*.{ts,tsx}: UsecreateScopedLoggerfor logging in backend TypeScript files
Typically add the logger initialization at the top of the file when usingcreateScopedLogger
Only use.with()on a logger instance within a specific function, not for a global loggerImport Prisma in the project using
import prisma from "@/utils/prisma";
**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.
Files:
apps/web/app/api/google/webhook/process-history-item.tsapps/web/app/api/google/webhook/process-history.tsapps/web/app/api/google/webhook/process-history-item.test.ts
apps/web/app/api/**/*.{ts,js}
📄 CodeRabbit Inference Engine (.cursor/rules/security-audit.mdc)
apps/web/app/api/**/*.{ts,js}: All API route handlers in 'apps/web/app/api/' must use authentication middleware: withAuth, withEmailAccount, or withError (with custom authentication logic).
All Prisma queries in API routes must include user/account filtering (e.g., emailAccountId or userId in WHERE clauses) to prevent unauthorized data access.
All parameters used in API routes must be validated before use; do not use parameters from 'params' or request bodies directly in queries without validation.
Request bodies in API routes should use Zod schemas for validation.
API routes should only return necessary fields using Prisma's 'select' and must not include sensitive data in error messages.
Error messages in API routes must not reveal internal details; use generic errors and SafeError for user-facing errors.
All QStash endpoints (API routes called via publishToQstash or publishToQstashQueue) must use verifySignatureAppRouter to verify request authenticity.
All cron endpoints in API routes must use hasCronSecret or hasPostCronSecret for authentication.
Do not hardcode weak or plaintext secrets in API route files; secrets must not be directly assigned as string literals.
Review all new withError usage in API routes to ensure custom authentication is implemented where required.
Files:
apps/web/app/api/google/webhook/process-history-item.tsapps/web/app/api/google/webhook/process-history.tsapps/web/app/api/google/webhook/process-history-item.test.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useelements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...
Files:
apps/web/app/api/google/webhook/process-history-item.tsapps/web/app/api/google/webhook/process-history.tsapps/web/app/api/google/webhook/process-history-item.test.ts
!pages/_document.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)
!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.
Files:
apps/web/app/api/google/webhook/process-history-item.tsapps/web/app/api/google/webhook/process-history.tsapps/web/app/api/google/webhook/process-history-item.test.ts
**/*.test.{ts,js}
📄 CodeRabbit Inference Engine (.cursor/rules/security.mdc)
Include security tests in your test suites to verify authentication, authorization, and error handling.
Files:
apps/web/app/api/google/webhook/process-history-item.test.ts
**/*.test.{ts,js,tsx,jsx}
📄 CodeRabbit Inference Engine (.cursor/rules/testing.mdc)
**/*.test.{ts,js,tsx,jsx}: Tests are colocated next to the tested file (e.g.,dir/format.tsanddir/format.test.ts)
Usevi.mock("server-only", () => ({}));to mock theserver-onlymodule in tests
Mock@/utils/prismain tests usingvi.mock("@/utils/prisma")and use the provided prisma mock
Mock external dependencies in tests
Clean up mocks between tests
Do not mock the Logger
Files:
apps/web/app/api/google/webhook/process-history-item.test.ts
**/*.{test,spec}.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)
**/*.{test,spec}.{js,jsx,ts,tsx}: Don't use export or module.exports in test files.
Don't use focused tests.
Don't use disabled tests.
Make sure the assertion function, like expect, is placed inside an it() function call.
Don't nest describe() blocks too deeply in test files.
Don't use focused tests.
Don't use disabled tests.
Don't use export or module.exports in test files.
Files:
apps/web/app/api/google/webhook/process-history-item.test.ts
🧠 Learnings (4)
📚 Learning: 2025-07-18T15:05:34.899Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-07-18T15:05:34.899Z
Learning: Applies to apps/web/utils/gmail/**/*.ts : Keep provider-specific implementation details isolated in the appropriate utils subfolder (e.g., 'apps/web/utils/gmail/')
Applied to files:
apps/web/app/api/google/webhook/process-history-item.ts
📚 Learning: 2025-06-23T12:27:30.570Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-06-23T12:27:30.570Z
Learning: When mocking Prisma in Vitest, import the Prisma mock from '@/utils/__mocks__/prisma', mock '@/utils/prisma', and clear all mocks in a beforeEach hook to ensure test isolation.
Applied to files:
apps/web/app/api/google/webhook/process-history-item.test.ts
📚 Learning: 2025-07-19T17:50:28.270Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-07-19T17:50:28.270Z
Learning: The `utils` folder also contains core app logic such as Next.js Server Actions and Gmail API requests.
Applied to files:
apps/web/app/api/google/webhook/process-history-item.test.ts
📚 Learning: 2025-06-23T12:26:53.882Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/prisma.mdc:0-0
Timestamp: 2025-06-23T12:26:53.882Z
Learning: In this project, Prisma should be imported using 'import prisma from "@/utils/prisma";' in TypeScript files.
Applied to files:
apps/web/app/api/google/webhook/process-history-item.test.ts
🧬 Code Graph Analysis (1)
apps/web/app/api/google/webhook/process-history-item.ts (4)
apps/web/utils/email/provider.ts (1)
createEmailProvider(9-24)apps/web/utils/llms/types.ts (1)
EmailAccountWithAI(10-29)apps/web/utils/email.ts (1)
extractEmailAddress(19-52)apps/web/utils/rule/learned-patterns.ts (1)
saveLearnedPatterns(73-175)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Static Code Analysis Js
- GitHub Check: Jit Security
🔇 Additional comments (16)
apps/web/app/api/google/webhook/process-history.ts (2)
165-165: LGTM! The addition oflabelRemovedto history types enables label-based learning.This change appropriately extends the history processing to capture label removal events, which is essential for the learning feature when users manually remove labels.
235-235: LGTM! Including labelsRemoved in historyMessages extends event collection.The addition properly includes label removal events in the processing pipeline.
apps/web/app/api/google/webhook/process-history-item.ts (9)
4-4: LGTM! Import additions support label resolution functionality.The addition of
getLabelByIdimport is necessary for resolving label names from IDs in the removal flow.
14-14: LGTM! ActionType import enables proper type checking.Adding ActionType ensures type-safe comparison when checking for LABEL actions.
28-30: LGTM! New imports support the label removal learning feature.The imports for
saveLearnedPatterns,EmailProvidertype, andinboxZeroLabelsare properly added to support the new learning functionality.
33-36: LGTM! Extended type signature properly handles all history event types.The function signature correctly accepts all three types of history events that can be processed.
60-72: Good architectural decision to create the email provider early and reuse it.Creating the email provider once and passing it to downstream functions prevents redundant provider instantiation and improves efficiency.
120-120: LGTM! Consistent provider passing eliminates redundancy.Passing the pre-created email provider to
processAssistantEmail,runColdEmailBlockerWithProvider, andrunRulesis a good improvement that prevents duplicate provider creation.Also applies to: 174-174, 214-214
305-366: Well-structured label removal handler with proper error handling.The function correctly processes label removal events, resolves label names, and handles errors gracefully.
395-418: LGTM! Cold email label removal handling is implemented correctly.The function properly handles cold email label removal by upserting the ColdEmail record with USER_REJECTED_COLD status, which will inform future cold email detection.
447-449: Good defensive check for matching label action.The validation ensures we only learn from labels that were actually applied by our rules, preventing incorrect learning from manually applied labels.
apps/web/app/api/google/webhook/process-history-item.test.ts (5)
6-6: LGTM! Import includes ColdEmailStatus for assertions.The addition of
ColdEmailStatusimport is necessary for the new test assertions.
19-22: LGTM! Test mocks properly configured for new dependencies.The mocks for
prisma,saveLearnedPatterns,createEmailProvider, andinboxZeroLabelsare correctly set up to support the label removal learning tests.
98-112: LGTM! Well-structured label mock implementation.The
getLabelByIdmock provides a clear mapping of label IDs to names, making tests readable and maintainable.
443-573: Comprehensive test coverage for label removal learning scenarios!The test suite thoroughly covers all key scenarios:
- Cold email label removal and status update
- Learning exclusion patterns from regular label removal
- Skipping learning for TO_REPLY rules
- Handling missing executed rules
The tests properly verify both the expected actions and the absence of unintended side effects.
187-187: LGTM! Test updated to use the module mock instead of dynamic mock.The removal of the dynamic mock for
createEmailProviderand reliance on the module mock improves test consistency.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (5)
apps/web/app/api/google/webhook/process-history.ts (1)
171-173: Use the constants to derive historyTypes to prevent string driftMinor DX improvement: derive from HistoryEventType values so future changes stay in sync.
- historyTypes: ["messageAdded", "labelAdded", "labelRemoved"], + historyTypes: Object.values(HistoryEventType),apps/web/app/api/google/webhook/process-history-item.ts (4)
28-32: Avoid circular dependency on process-history by moving HistoryEventType to a shared types moduleprocess-history.ts imports process-history-item.ts, and this file imports HistoryEventType back from process-history.ts. While this often works in ESM with live bindings, it’s fragile and can bite during bundling. Prefer exporting HistoryEventType from "@/app/api/google/webhook/types" (next to ProcessHistoryOptions) and import both from there.
I can generate the move + import updates across both files if you want.
65-81: Instantiate emailProvider lazily to avoid unnecessary work on LABEL_ADDED no-op pathCurrently the provider is created even when type === LABEL_ADDED (and you immediately return). You can defer instantiation to the branches that actually need it, reducing overhead.
Outline:
- For LABEL_ADDED: log and return early (no provider).
- For LABEL_REMOVED: create provider inside the branch (needed to fetch the message).
- For MESSAGE_ADDED (default case): create provider before use as you already do.
I can provide a concrete diff touching this and the first provider usage site if you want to proceed.
314-375: Solid implementation of label removal handling; consider minor enhancementsThe flow is sound: fetch message, resolve removed label names, and dispatch learning. Two optional improvements:
- Short-circuit when removedLabelIds.length === 0 to avoid a no-op Promise.all.
- If you expect many labels, you could parallelize learnFromRemovedLabel calls with Promise.all for throughput (current sequential await is okay given likely small cardinality).
377-496: Learning logic looks correct; aligns removed label to matching rule action and stores exclusion
- Correctly upserts ColdEmail with USER_REJECTED_COLD when the cold email label is removed.
- Guards against learning when no executed rule or no matching LABEL action is present.
- Skips TO_REPLY system type, which makes sense.
One tiny nit: sender is typed as string | null but extractEmailAddress returns string (empty string on failure). The existing falsy check still works; you could simplify the type to string for clarity.
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
apps/web/app/api/google/webhook/process-history-item.test.ts(5 hunks)apps/web/app/api/google/webhook/process-history-item.ts(9 hunks)apps/web/app/api/google/webhook/process-history.ts(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/web/app/api/google/webhook/process-history-item.test.ts
🧰 Additional context used
📓 Path-based instructions (8)
apps/web/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use@/for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX
Files:
apps/web/app/api/google/webhook/process-history.tsapps/web/app/api/google/webhook/process-history-item.ts
apps/web/app/**
📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)
NextJS app router structure with (app) directory
Files:
apps/web/app/api/google/webhook/process-history.tsapps/web/app/api/google/webhook/process-history-item.ts
!{.cursor/rules/*.mdc}
📄 CodeRabbit Inference Engine (.cursor/rules/cursor-rules.mdc)
Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location
Files:
apps/web/app/api/google/webhook/process-history.tsapps/web/app/api/google/webhook/process-history-item.ts
**/*.ts
📄 CodeRabbit Inference Engine (.cursor/rules/form-handling.mdc)
**/*.ts: The same validation should be done in the server action too
Define validation schemas using Zod
Files:
apps/web/app/api/google/webhook/process-history.tsapps/web/app/api/google/webhook/process-history-item.ts
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/logging.mdc)
**/*.{ts,tsx}: UsecreateScopedLoggerfor logging in backend TypeScript files
Typically add the logger initialization at the top of the file when usingcreateScopedLogger
Only use.with()on a logger instance within a specific function, not for a global loggerImport Prisma in the project using
import prisma from "@/utils/prisma";
**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.
Files:
apps/web/app/api/google/webhook/process-history.tsapps/web/app/api/google/webhook/process-history-item.ts
apps/web/app/api/**/*.{ts,js}
📄 CodeRabbit Inference Engine (.cursor/rules/security-audit.mdc)
apps/web/app/api/**/*.{ts,js}: All API route handlers in 'apps/web/app/api/' must use authentication middleware: withAuth, withEmailAccount, or withError (with custom authentication logic).
All Prisma queries in API routes must include user/account filtering (e.g., emailAccountId or userId in WHERE clauses) to prevent unauthorized data access.
All parameters used in API routes must be validated before use; do not use parameters from 'params' or request bodies directly in queries without validation.
Request bodies in API routes should use Zod schemas for validation.
API routes should only return necessary fields using Prisma's 'select' and must not include sensitive data in error messages.
Error messages in API routes must not reveal internal details; use generic errors and SafeError for user-facing errors.
All QStash endpoints (API routes called via publishToQstash or publishToQstashQueue) must use verifySignatureAppRouter to verify request authenticity.
All cron endpoints in API routes must use hasCronSecret or hasPostCronSecret for authentication.
Do not hardcode weak or plaintext secrets in API route files; secrets must not be directly assigned as string literals.
Review all new withError usage in API routes to ensure custom authentication is implemented where required.
Files:
apps/web/app/api/google/webhook/process-history.tsapps/web/app/api/google/webhook/process-history-item.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useelements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...
Files:
apps/web/app/api/google/webhook/process-history.tsapps/web/app/api/google/webhook/process-history-item.ts
!pages/_document.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)
!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.
Files:
apps/web/app/api/google/webhook/process-history.tsapps/web/app/api/google/webhook/process-history-item.ts
🧠 Learnings (1)
📚 Learning: 2025-07-18T15:05:34.899Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-07-18T15:05:34.899Z
Learning: Applies to apps/web/utils/gmail/**/*.ts : Keep provider-specific implementation details isolated in the appropriate utils subfolder (e.g., 'apps/web/utils/gmail/')
Applied to files:
apps/web/app/api/google/webhook/process-history-item.ts
🧬 Code Graph Analysis (2)
apps/web/app/api/google/webhook/process-history.ts (2)
apps/web/app/api/google/webhook/process-history-item.ts (1)
processHistoryItem(33-243)apps/web/utils/error.ts (1)
captureException(63-75)
apps/web/app/api/google/webhook/process-history-item.ts (4)
apps/web/utils/email/provider.ts (1)
createEmailProvider(9-24)apps/web/utils/llms/types.ts (1)
EmailAccountWithAI(10-29)apps/web/utils/email.ts (1)
extractEmailAddress(19-52)apps/web/utils/rule/learned-patterns.ts (1)
saveLearnedPatterns(73-175)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Static Code Analysis Js
- GitHub Check: Jit Security
🔇 Additional comments (6)
apps/web/app/api/google/webhook/process-history.ts (2)
239-242: LGTM: Include labelsRemoved in the “has work” checkGood catch ensuring label removals also trigger processing when no messagesAdded/labelsAdded are present.
262-275: LGTM: Updated error context references and processingUsing the wrapped item and logging by item.item.message?.id/threadId aligns with the new event model.
apps/web/app/api/google/webhook/process-history-item.ts (4)
33-41: LGTM: Discriminated wrapper item in signature is clear and extensibleThe new wrappedItem signature cleanly separates event types and supports label-based flows.
50-53: LGTM: Early extraction of messageId/threadId and guardClear and consistent with downstream usage.
70-81: LGTM: LABEL_REMOVED flow is properly branched from normal processingGood separation; prevents rule engine from running when only learning is needed.
183-186: LGTM: Pass emailProvider through to cold-email blockerConsistent with the provider-based refactor and avoids duplicate client creation.
|
|
||
| const inboxMessages = historyMessages.filter(isInboxOrSentMessage); | ||
| const uniqueMessages = uniqBy(inboxMessages, (m) => m.message?.id); | ||
| const allEvents = [ |
There was a problem hiding this comment.
Adding a message type field here, wrapping the event item, so it is possible to discover the actual event tied to the message in the underlying functions.
Mainly to differentiate between LABEL_ADDED vs LABEL_REMOVED events inside processHistoryItem
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
apps/web/app/api/google/webhook/process-history-item.ts (1)
354-361: Consider optimizing label resolution for better performance.The current approach makes individual API calls for each label. For better performance when multiple labels are removed, consider batching label requests if the Gmail API supports it.
- const removedLabels = await Promise.all( - removedLabelIds.map((labelId: string) => - getLabelById({ gmail, id: labelId }), - ), - ); + // Consider implementing batch label resolution if Gmail API supports it + const removedLabels = await Promise.all( + removedLabelIds.map((labelId: string) => + getLabelById({ gmail, id: labelId }), + ), + );
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
apps/web/app/api/google/webhook/process-history-item.ts(7 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
apps/web/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use@/for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX
Files:
apps/web/app/api/google/webhook/process-history-item.ts
apps/web/app/**
📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)
NextJS app router structure with (app) directory
Files:
apps/web/app/api/google/webhook/process-history-item.ts
!{.cursor/rules/*.mdc}
📄 CodeRabbit Inference Engine (.cursor/rules/cursor-rules.mdc)
Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location
Files:
apps/web/app/api/google/webhook/process-history-item.ts
**/*.ts
📄 CodeRabbit Inference Engine (.cursor/rules/form-handling.mdc)
**/*.ts: The same validation should be done in the server action too
Define validation schemas using Zod
Files:
apps/web/app/api/google/webhook/process-history-item.ts
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/logging.mdc)
**/*.{ts,tsx}: UsecreateScopedLoggerfor logging in backend TypeScript files
Typically add the logger initialization at the top of the file when usingcreateScopedLogger
Only use.with()on a logger instance within a specific function, not for a global loggerImport Prisma in the project using
import prisma from "@/utils/prisma";
**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.
Files:
apps/web/app/api/google/webhook/process-history-item.ts
apps/web/app/api/**/*.{ts,js}
📄 CodeRabbit Inference Engine (.cursor/rules/security-audit.mdc)
apps/web/app/api/**/*.{ts,js}: All API route handlers in 'apps/web/app/api/' must use authentication middleware: withAuth, withEmailAccount, or withError (with custom authentication logic).
All Prisma queries in API routes must include user/account filtering (e.g., emailAccountId or userId in WHERE clauses) to prevent unauthorized data access.
All parameters used in API routes must be validated before use; do not use parameters from 'params' or request bodies directly in queries without validation.
Request bodies in API routes should use Zod schemas for validation.
API routes should only return necessary fields using Prisma's 'select' and must not include sensitive data in error messages.
Error messages in API routes must not reveal internal details; use generic errors and SafeError for user-facing errors.
All QStash endpoints (API routes called via publishToQstash or publishToQstashQueue) must use verifySignatureAppRouter to verify request authenticity.
All cron endpoints in API routes must use hasCronSecret or hasPostCronSecret for authentication.
Do not hardcode weak or plaintext secrets in API route files; secrets must not be directly assigned as string literals.
Review all new withError usage in API routes to ensure custom authentication is implemented where required.
Files:
apps/web/app/api/google/webhook/process-history-item.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useelements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...
Files:
apps/web/app/api/google/webhook/process-history-item.ts
!pages/_document.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)
!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.
Files:
apps/web/app/api/google/webhook/process-history-item.ts
🧠 Learnings (1)
📚 Learning: 2025-07-18T15:05:34.899Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-07-18T15:05:34.899Z
Learning: Applies to apps/web/utils/gmail/**/*.ts : Keep provider-specific implementation details isolated in the appropriate utils subfolder (e.g., 'apps/web/utils/gmail/')
Applied to files:
apps/web/app/api/google/webhook/process-history-item.ts
🧬 Code Graph Analysis (1)
apps/web/app/api/google/webhook/process-history-item.ts (4)
apps/web/utils/email/provider.ts (1)
createEmailProvider(9-24)apps/web/utils/llms/types.ts (1)
EmailAccountWithAI(10-29)apps/web/utils/email.ts (1)
extractEmailAddress(19-52)apps/web/utils/rule/learned-patterns.ts (1)
saveLearnedPatterns(73-175)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Static Code Analysis Js
- GitHub Check: Jit Security
🔇 Additional comments (8)
apps/web/app/api/google/webhook/process-history-item.ts (8)
33-40: Function signature update looks good for handling different history event types.The new wrapped structure properly accommodates the different event types (messageAdded, labelAdded, labelRemoved) while maintaining type safety. The union type ensures proper handling of each event variant.
65-68: Good optimization: Creating EmailProvider early in the flow.Moving the provider creation earlier prevents duplicate provider instantiation and makes it available for both label event handling and regular message processing.
70-81: Early return pattern is appropriate for label events.The conditional handling correctly branches between different event types. The TODO comment for
handleLabelAddedEventis noted and appropriate for future implementation.
314-375: Solid implementation for label removal event handling.The function properly:
- Validates required fields (messageId, threadId)
- Retrieves message content and resolves label names
- Handles multiple removed labels in a loop
- Includes comprehensive error handling and logging
377-496: Comprehensive learning logic with proper validation.The function implements thorough validation:
- Checks for sender presence (required for learning)
- Handles special cold email label removal case
- Validates executed rules exist for the message
- Ensures label action matches the removed label
- Excludes TO_REPLY system rules from learning
The error handling and logging throughout is comprehensive.
404-427: Cold email handling logic is correct.The upsert operation properly handles both existing and new cold email records, setting the status to
USER_REJECTED_COLDwhen users remove the cold email label.
456-468: Robust validation for rule-based learning.The check ensures learning only occurs for labels that were applied by automation rules, preventing false learning from manually applied/removed labels.
479-489: Proper learned pattern persistence.The saveLearnedPatterns call correctly creates an exclusion pattern for the sender, using the
FROMtype andexclude: trueflag as expected.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
apps/web/app/api/google/webhook/types.ts (1)
6-14: Great enum replacement and centralization into shared typesUsing a const object with a companion type alias aligns with the “no TS enums” guideline and avoids circular deps. Centralizing HistoryEventType here improves reuse across the webhook modules.
apps/web/app/api/google/webhook/process-history.ts (1)
243-260: Correct: build unified events + dedupe by (type, messageId)The unified events array covers all three event types, and the composite key in uniqBy preserves multiple event-types for the same message. This addresses the previously flagged drop-issue when deduping by message only.
🧹 Nitpick comments (4)
apps/web/app/api/google/webhook/process-history-item.test.ts (1)
99-113: Potentially unused getLabelById mock in this test suiteThis test file focuses on processHistoryItem and doesn’t invoke the label-removed handler that calls Gmail label listing. The getLabelById mock may be dead code here; consider removing to keep the test lean.
apps/web/app/api/google/webhook/process-label-removed-event.test.ts (2)
70-70: Remove console.log in tests (project guideline: don’t use console)Drop the debug log inside the labels.list mock.
Apply this diff:
- console.log("Mock gmail.users.labels.list called with:", params);
109-116: Remove console logging and unnecessary try/catch in testPer guidelines, avoid console.* in tests. Also, the try/catch rethrows and adds no value here.
Apply this diff:
- console.log("Test data:", JSON.stringify(historyItem.item, null, 2)); - - try { - await handleLabelRemovedEvent(historyItem.item, defaultOptions); - } catch (error) { - console.error("Function error:", error); - throw error; - } + await handleLabelRemovedEvent(historyItem.item, defaultOptions);apps/web/app/api/google/webhook/process-label-removed-event.ts (1)
63-71: Optional: cache label map to avoid repeated list calls per label-removed eventEach event triggers a labels.list call and then a linear search per id. If history batches include many labelRemoved items, consider building a label-id→name map once per invocation and reusing it across ids (or pass a cached map from the caller if processing multiple events for the same account).
Also applies to: 77-85
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (6)
apps/web/app/api/google/webhook/process-history-item.test.ts(4 hunks)apps/web/app/api/google/webhook/process-history-item.ts(4 hunks)apps/web/app/api/google/webhook/process-history.ts(3 hunks)apps/web/app/api/google/webhook/process-label-removed-event.test.ts(1 hunks)apps/web/app/api/google/webhook/process-label-removed-event.ts(1 hunks)apps/web/app/api/google/webhook/types.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/web/app/api/google/webhook/process-history-item.ts
🧰 Additional context used
📓 Path-based instructions (11)
apps/web/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use@/for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX
Files:
apps/web/app/api/google/webhook/types.tsapps/web/app/api/google/webhook/process-label-removed-event.test.tsapps/web/app/api/google/webhook/process-label-removed-event.tsapps/web/app/api/google/webhook/process-history.tsapps/web/app/api/google/webhook/process-history-item.test.ts
apps/web/app/**
📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)
NextJS app router structure with (app) directory
Files:
apps/web/app/api/google/webhook/types.tsapps/web/app/api/google/webhook/process-label-removed-event.test.tsapps/web/app/api/google/webhook/process-label-removed-event.tsapps/web/app/api/google/webhook/process-history.tsapps/web/app/api/google/webhook/process-history-item.test.ts
!{.cursor/rules/*.mdc}
📄 CodeRabbit Inference Engine (.cursor/rules/cursor-rules.mdc)
Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location
Files:
apps/web/app/api/google/webhook/types.tsapps/web/app/api/google/webhook/process-label-removed-event.test.tsapps/web/app/api/google/webhook/process-label-removed-event.tsapps/web/app/api/google/webhook/process-history.tsapps/web/app/api/google/webhook/process-history-item.test.ts
**/*.ts
📄 CodeRabbit Inference Engine (.cursor/rules/form-handling.mdc)
**/*.ts: The same validation should be done in the server action too
Define validation schemas using Zod
Files:
apps/web/app/api/google/webhook/types.tsapps/web/app/api/google/webhook/process-label-removed-event.test.tsapps/web/app/api/google/webhook/process-label-removed-event.tsapps/web/app/api/google/webhook/process-history.tsapps/web/app/api/google/webhook/process-history-item.test.ts
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/logging.mdc)
**/*.{ts,tsx}: UsecreateScopedLoggerfor logging in backend TypeScript files
Typically add the logger initialization at the top of the file when usingcreateScopedLogger
Only use.with()on a logger instance within a specific function, not for a global loggerImport Prisma in the project using
import prisma from "@/utils/prisma";
**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.
Files:
apps/web/app/api/google/webhook/types.tsapps/web/app/api/google/webhook/process-label-removed-event.test.tsapps/web/app/api/google/webhook/process-label-removed-event.tsapps/web/app/api/google/webhook/process-history.tsapps/web/app/api/google/webhook/process-history-item.test.ts
apps/web/app/api/**/*.{ts,js}
📄 CodeRabbit Inference Engine (.cursor/rules/security-audit.mdc)
apps/web/app/api/**/*.{ts,js}: All API route handlers in 'apps/web/app/api/' must use authentication middleware: withAuth, withEmailAccount, or withError (with custom authentication logic).
All Prisma queries in API routes must include user/account filtering (e.g., emailAccountId or userId in WHERE clauses) to prevent unauthorized data access.
All parameters used in API routes must be validated before use; do not use parameters from 'params' or request bodies directly in queries without validation.
Request bodies in API routes should use Zod schemas for validation.
API routes should only return necessary fields using Prisma's 'select' and must not include sensitive data in error messages.
Error messages in API routes must not reveal internal details; use generic errors and SafeError for user-facing errors.
All QStash endpoints (API routes called via publishToQstash or publishToQstashQueue) must use verifySignatureAppRouter to verify request authenticity.
All cron endpoints in API routes must use hasCronSecret or hasPostCronSecret for authentication.
Do not hardcode weak or plaintext secrets in API route files; secrets must not be directly assigned as string literals.
Review all new withError usage in API routes to ensure custom authentication is implemented where required.
Files:
apps/web/app/api/google/webhook/types.tsapps/web/app/api/google/webhook/process-label-removed-event.test.tsapps/web/app/api/google/webhook/process-label-removed-event.tsapps/web/app/api/google/webhook/process-history.tsapps/web/app/api/google/webhook/process-history-item.test.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useelements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...
Files:
apps/web/app/api/google/webhook/types.tsapps/web/app/api/google/webhook/process-label-removed-event.test.tsapps/web/app/api/google/webhook/process-label-removed-event.tsapps/web/app/api/google/webhook/process-history.tsapps/web/app/api/google/webhook/process-history-item.test.ts
!pages/_document.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)
!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.
Files:
apps/web/app/api/google/webhook/types.tsapps/web/app/api/google/webhook/process-label-removed-event.test.tsapps/web/app/api/google/webhook/process-label-removed-event.tsapps/web/app/api/google/webhook/process-history.tsapps/web/app/api/google/webhook/process-history-item.test.ts
**/*.test.{ts,js}
📄 CodeRabbit Inference Engine (.cursor/rules/security.mdc)
Include security tests in your test suites to verify authentication, authorization, and error handling.
Files:
apps/web/app/api/google/webhook/process-label-removed-event.test.tsapps/web/app/api/google/webhook/process-history-item.test.ts
**/*.test.{ts,js,tsx,jsx}
📄 CodeRabbit Inference Engine (.cursor/rules/testing.mdc)
**/*.test.{ts,js,tsx,jsx}: Tests are colocated next to the tested file (e.g.,dir/format.tsanddir/format.test.ts)
Usevi.mock("server-only", () => ({}));to mock theserver-onlymodule in tests
Mock@/utils/prismain tests usingvi.mock("@/utils/prisma")and use the provided prisma mock
Mock external dependencies in tests
Clean up mocks between tests
Do not mock the Logger
Files:
apps/web/app/api/google/webhook/process-label-removed-event.test.tsapps/web/app/api/google/webhook/process-history-item.test.ts
**/*.{test,spec}.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)
**/*.{test,spec}.{js,jsx,ts,tsx}: Don't use export or module.exports in test files.
Don't use focused tests.
Don't use disabled tests.
Make sure the assertion function, like expect, is placed inside an it() function call.
Don't nest describe() blocks too deeply in test files.
Don't use focused tests.
Don't use disabled tests.
Don't use export or module.exports in test files.
Files:
apps/web/app/api/google/webhook/process-label-removed-event.test.tsapps/web/app/api/google/webhook/process-history-item.test.ts
🧠 Learnings (3)
📚 Learning: 2025-06-23T12:27:30.570Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-06-23T12:27:30.570Z
Learning: When mocking Prisma in Vitest, import the Prisma mock from '@/utils/__mocks__/prisma', mock '@/utils/prisma', and clear all mocks in a beforeEach hook to ensure test isolation.
Applied to files:
apps/web/app/api/google/webhook/process-history-item.test.ts
📚 Learning: 2025-07-19T17:50:28.270Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-07-19T17:50:28.270Z
Learning: The `utils` folder also contains core app logic such as Next.js Server Actions and Gmail API requests.
Applied to files:
apps/web/app/api/google/webhook/process-history-item.test.ts
📚 Learning: 2025-06-23T12:26:53.882Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/prisma.mdc:0-0
Timestamp: 2025-06-23T12:26:53.882Z
Learning: In this project, Prisma should be imported using 'import prisma from "@/utils/prisma";' in TypeScript files.
Applied to files:
apps/web/app/api/google/webhook/process-history-item.test.ts
🧬 Code Graph Analysis (3)
apps/web/app/api/google/webhook/process-label-removed-event.test.ts (3)
apps/web/app/api/google/webhook/types.ts (2)
HistoryEventType(6-10)HistoryEventType(12-13)apps/web/app/api/google/webhook/process-label-removed-event.ts (1)
handleLabelRemovedEvent(24-89)apps/web/utils/rule/learned-patterns.ts (1)
saveLearnedPatterns(73-175)
apps/web/app/api/google/webhook/process-history.ts (3)
apps/web/app/api/google/webhook/types.ts (2)
HistoryEventType(6-10)HistoryEventType(12-13)apps/web/app/api/google/webhook/process-history-item.ts (1)
processHistoryItem(31-240)apps/web/utils/error.ts (1)
captureException(63-75)
apps/web/app/api/google/webhook/process-history-item.test.ts (1)
apps/web/app/api/google/webhook/types.ts (2)
HistoryEventType(6-10)HistoryEventType(12-13)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Static Code Analysis Js
- GitHub Check: Jit Security
🔇 Additional comments (8)
apps/web/app/api/google/webhook/process-history.ts (3)
11-14: Importing event/type definitions from shared types prevents cyclesGood move sourcing HistoryEventType and ProcessHistoryOptions from a dedicated types module.
164-170: Including labelRemoved in Gmail history fetchRequesting "labelRemoved" events is required for the new learning flow; this looks correct.
262-275: Iterating and error-reporting on event shape is soundPassing the wrapped event into processHistoryItem and logging by event.item.message? id/threadId keeps diagnostics accurate across event types.
apps/web/app/api/google/webhook/process-history-item.test.ts (1)
124-154: Helper for generating history items is clear and type-safeGood coverage for MESSAGE_ADDED, LABEL_ADDED, and LABEL_REMOVED shapes with explicit gmail_v1 schema types.
apps/web/app/api/google/webhook/process-label-removed-event.test.ts (1)
18-43: Mocks are well-scoped for this unit (labels + enums + extractEmail)GmailLabel constants and label name mapping provide the right isolation for handleLabelRemovedEvent.
apps/web/app/api/google/webhook/process-label-removed-event.ts (3)
118-141: Cold Email rejection upsert path looks correctUpserting by (emailAccountId, fromEmail) and setting USER_REJECTED_COLD is the right effect for Cold Email label removal.
143-189: Executed rule lookup and action match are appropriate guardsLooking up the executed rule by (emailAccountId, threadId, messageId) and ensuring a matching LABEL action prevents learning from user-applied labels.
198-217: Learned exclusion pattern persisted with exclude flagSaving a FROM pattern with exclude: true aligns with the intended “teach to exclude this sender” behavior.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
apps/web/app/api/google/webhook/process-label-removed-event.ts (1)
61-76: Skip Gmail label listing if no IDs and speed up ID→name mappingNice job fixing the previous predicate bug. Two small improvements:
- Return early when no removedLabelIds to avoid an unnecessary Gmail API call.
- Build a labelId→name map once to avoid O(n*m) lookups.
Apply this diff:
- const removedLabelIds = message.labelIds || []; - - const labels = await gmail.users.labels.list({ userId: "me" }); - - const removedLabelNames = removedLabelIds - .map((labelId: string) => { - const label = labels.data.labels?.find( - (l: gmail_v1.Schema$Label) => l.id === labelId, - ); - return label?.name; - }) - .filter( - (labelName: string | null | undefined): labelName is string => - !!labelName && !SYSTEM_LABELS.includes(labelName), - ); + const removedLabelIds = message.labelIds || []; + if (removedLabelIds.length === 0) { + logger.info("No labels removed in event; skipping", loggerOptions); + return; + } + + const labels = await gmail.users.labels.list({ userId: "me" }); + const allLabels = labels.data.labels ?? []; + const idToName = new Map( + allLabels + .filter( + (l): l is gmail_v1.Schema$Label & { id: string; name: string } => + typeof l.id === "string" && typeof l.name === "string", + ) + .map((l) => [l.id, l.name] as const), + ); + + const removedLabelNames = removedLabelIds + .map((labelId: string) => idToName.get(labelId)) + .filter( + (labelName: string | undefined): labelName is string => + typeof labelName === "string" && !SYSTEM_LABELS.includes(labelName), + );
🧹 Nitpick comments (5)
apps/web/app/api/google/webhook/process-label-removed-event.ts (5)
58-60: Use null-safe access for headers.from to satisfy strict null checksDepending on the provider, headers or from can be undefined. Make this null-safe to align with strict null checks and avoid accidental any.
Apply this diff:
- const parsedMessage = await provider.getMessage(messageId); - const sender = extractEmailAddress(parsedMessage.headers.from); + const parsedMessage = await provider.getMessage(messageId); + const from = parsedMessage?.headers?.from ?? ""; + const sender = extractEmailAddress(from);
77-85: Process removed labels concurrentlyEach iteration is independent; you can reduce total latency by running them in parallel. Keep in mind DB concurrency limits, but for a handful of labels this is safe.
- for (const labelName of removedLabelNames) { - await learnFromRemovedLabel({ - labelName, - sender, - messageId, - threadId, - emailAccountId, - }); - } + await Promise.all( + removedLabelNames.map((labelName) => + learnFromRemovedLabel({ + labelName, + sender, + messageId, + threadId, + emailAccountId, + }), + ), + );
13-22: Consider including Gmail “category” labels in SYSTEM_LABELSGmail’s CATEGORY_* labels (Personal, Social, Promotions, Forums, Updates) are also system-managed. If users remove these, do we want to skip learning as well? If yes, include them to avoid false positives.
const SYSTEM_LABELS = [ GmailLabel.INBOX, GmailLabel.SENT, GmailLabel.DRAFT, GmailLabel.SPAM, GmailLabel.TRASH, GmailLabel.IMPORTANT, GmailLabel.STARRED, GmailLabel.UNREAD, + GmailLabel.PERSONAL, + GmailLabel.SOCIAL, + GmailLabel.PROMOTIONS, + GmailLabel.FORUMS, + GmailLabel.UPDATES, ];
193-196: Avoid magic strings for rule.systemType; use Prisma enum for type-safetyLeverage the generated Prisma enum (consistent with ActionType usage) to prevent typos and ease refactors.
-import { ActionType, ColdEmailStatus } from "@prisma/client"; +import { ActionType, ColdEmailStatus, RuleSystemType } from "@prisma/client";- if (executedRule.rule.systemType === "TO_REPLY") { + if (executedRule.rule.systemType === RuleSystemType.TO_REPLY) {Also applies to: 4-4
104-111: Optional: minimize PII in logsloggerOptions includes sender for downstream logs. Consider omitting or masking the sender in info/error logs, unless it’s strictly needed for debugging and logs are adequately protected.
Would you like a small helper to redact emails in logger metadata?
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
apps/web/app/api/google/webhook/process-label-removed-event.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
apps/web/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use@/for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX
Files:
apps/web/app/api/google/webhook/process-label-removed-event.ts
apps/web/app/**
📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)
NextJS app router structure with (app) directory
Files:
apps/web/app/api/google/webhook/process-label-removed-event.ts
!{.cursor/rules/*.mdc}
📄 CodeRabbit Inference Engine (.cursor/rules/cursor-rules.mdc)
Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location
Files:
apps/web/app/api/google/webhook/process-label-removed-event.ts
**/*.ts
📄 CodeRabbit Inference Engine (.cursor/rules/form-handling.mdc)
**/*.ts: The same validation should be done in the server action too
Define validation schemas using Zod
Files:
apps/web/app/api/google/webhook/process-label-removed-event.ts
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/logging.mdc)
**/*.{ts,tsx}: UsecreateScopedLoggerfor logging in backend TypeScript files
Typically add the logger initialization at the top of the file when usingcreateScopedLogger
Only use.with()on a logger instance within a specific function, not for a global loggerImport Prisma in the project using
import prisma from "@/utils/prisma";
**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.
Files:
apps/web/app/api/google/webhook/process-label-removed-event.ts
apps/web/app/api/**/*.{ts,js}
📄 CodeRabbit Inference Engine (.cursor/rules/security-audit.mdc)
apps/web/app/api/**/*.{ts,js}: All API route handlers in 'apps/web/app/api/' must use authentication middleware: withAuth, withEmailAccount, or withError (with custom authentication logic).
All Prisma queries in API routes must include user/account filtering (e.g., emailAccountId or userId in WHERE clauses) to prevent unauthorized data access.
All parameters used in API routes must be validated before use; do not use parameters from 'params' or request bodies directly in queries without validation.
Request bodies in API routes should use Zod schemas for validation.
API routes should only return necessary fields using Prisma's 'select' and must not include sensitive data in error messages.
Error messages in API routes must not reveal internal details; use generic errors and SafeError for user-facing errors.
All QStash endpoints (API routes called via publishToQstash or publishToQstashQueue) must use verifySignatureAppRouter to verify request authenticity.
All cron endpoints in API routes must use hasCronSecret or hasPostCronSecret for authentication.
Do not hardcode weak or plaintext secrets in API route files; secrets must not be directly assigned as string literals.
Review all new withError usage in API routes to ensure custom authentication is implemented where required.
Files:
apps/web/app/api/google/webhook/process-label-removed-event.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useelements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...
Files:
apps/web/app/api/google/webhook/process-label-removed-event.ts
!pages/_document.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)
!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.
Files:
apps/web/app/api/google/webhook/process-label-removed-event.ts
🧬 Code Graph Analysis (1)
apps/web/app/api/google/webhook/process-label-removed-event.ts (5)
apps/web/utils/gmail/label.ts (1)
GmailLabel(20-34)apps/web/utils/llms/types.ts (1)
EmailAccountWithAI(10-29)apps/web/app/api/outlook/webhook/logger.ts (1)
logger(3-3)apps/web/utils/email.ts (1)
extractEmailAddress(19-52)apps/web/utils/rule/learned-patterns.ts (1)
saveLearnedPatterns(73-175)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Static Code Analysis Js
- GitHub Check: Jit Security
🔇 Additional comments (3)
apps/web/app/api/google/webhook/process-label-removed-event.ts (3)
47-53: Good early exit on missing identifiersBailing out when messageId/threadId are absent prevents downstream errors and noisy logs. Looks solid.
118-141: Cold email upsert path looks correctThe upsert correctly marks USER_REJECTED_COLD and short-circuits further learning. Nice.
200-210: Good: learned patterns saved as exclusion on FROMThis aligns with the “user removed label => exclude sender from that rule” intent. Wrapped in try/catch with scoped logging—good.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
apps/web/app/api/google/webhook/process-label-removed-event.ts (1)
65-75: Fix filter type guard: current predicate can fail TS narrowing and re-allow non-stringsUse a proper type guard to ensure only non-system string names are kept. This also resolves the compile-time error from passing null/undefined to Array.includes under strict null checks.
Apply this diff:
- .filter( - (labelName: string | null | undefined): labelName is string => - !!labelName && !SYSTEM_LABELS.includes(labelName), - ); + .filter( + (labelName: string | null | undefined): labelName is string => + typeof labelName === "string" && !SYSTEM_LABELS.includes(labelName), + );
🧹 Nitpick comments (5)
apps/web/app/api/google/webhook/process-label-removed-event.ts (5)
58-60: Guard against missing headers to avoid unnecessary exceptionsIf provider.getMessage() omits headers or from, this will throw. Prefer a safe fallback and let learnFromRemovedLabel short-circuit via the existing sender check.
- const parsedMessage = await provider.getMessage(messageId); - const sender = extractEmailAddress(parsedMessage.headers.from); + const parsedMessage = await provider.getMessage(messageId); + const sender = + extractEmailAddress(parsedMessage?.headers?.from ?? "") || null;
61-62: Prefer nullish coalescing over logical ORSemantically clearer for optional arrays and avoids surprising behavior if labelIds were ever an empty string (unlikely but aligns with TS intent).
- const removedLabelIds = message.labelIds || []; + const removedLabelIds = message.labelIds ?? [];
77-85: Process multiple removed labels concurrentlyThis reduces latency when many labels are removed. Errors still propagate and are caught by the outer try/catch.
- for (const labelName of removedLabelNames) { - await learnFromRemovedLabel({ - labelName, - sender, - messageId, - threadId, - emailAccountId, - }); - } + await Promise.all( + removedLabelNames.map((labelName) => + learnFromRemovedLabel({ + labelName, + sender, + messageId, + threadId, + emailAccountId, + }), + ), + );
118-141: Non-cold labels are no-ops; is saveLearnedPatterns meant to be invoked here?Given the PR’s goal (learning patterns from labels) and the imported saveLearnedPatterns, it looks like non-cold label removals should trigger learning. Currently, the function returns without any action unless the cold email label is removed.
If the intent is to learn exclusions from other labels (e.g., map labelName -> an ActionType for sender or domain), I can wire this up. Share the target behavior and saveLearnedPatterns signature, and I’ll provide a concrete patch.
41-46: Minor: consider using logger.with(loggerOptions) inside each functionCreate a per-function scoped logger to avoid repeating loggerOptions and keep call sites concise:
Example:
const log = logger.with(loggerOptions); log.warn("Skipping label removal - missing messageId or threadId"); log.info("Processing label removal for learning"); log.error("Error processing label removal", { error });Same pattern applies in learnFromRemovedLabel.
Also applies to: 104-111
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
apps/web/app/api/google/webhook/process-label-removed-event.test.ts(1 hunks)apps/web/app/api/google/webhook/process-label-removed-event.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/web/app/api/google/webhook/process-label-removed-event.test.ts
🧰 Additional context used
📓 Path-based instructions (8)
apps/web/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use@/for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX
Files:
apps/web/app/api/google/webhook/process-label-removed-event.ts
apps/web/app/**
📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)
NextJS app router structure with (app) directory
Files:
apps/web/app/api/google/webhook/process-label-removed-event.ts
!{.cursor/rules/*.mdc}
📄 CodeRabbit Inference Engine (.cursor/rules/cursor-rules.mdc)
Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location
Files:
apps/web/app/api/google/webhook/process-label-removed-event.ts
**/*.ts
📄 CodeRabbit Inference Engine (.cursor/rules/form-handling.mdc)
**/*.ts: The same validation should be done in the server action too
Define validation schemas using Zod
Files:
apps/web/app/api/google/webhook/process-label-removed-event.ts
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/logging.mdc)
**/*.{ts,tsx}: UsecreateScopedLoggerfor logging in backend TypeScript files
Typically add the logger initialization at the top of the file when usingcreateScopedLogger
Only use.with()on a logger instance within a specific function, not for a global loggerImport Prisma in the project using
import prisma from "@/utils/prisma";
**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.
Files:
apps/web/app/api/google/webhook/process-label-removed-event.ts
apps/web/app/api/**/*.{ts,js}
📄 CodeRabbit Inference Engine (.cursor/rules/security-audit.mdc)
apps/web/app/api/**/*.{ts,js}: All API route handlers in 'apps/web/app/api/' must use authentication middleware: withAuth, withEmailAccount, or withError (with custom authentication logic).
All Prisma queries in API routes must include user/account filtering (e.g., emailAccountId or userId in WHERE clauses) to prevent unauthorized data access.
All parameters used in API routes must be validated before use; do not use parameters from 'params' or request bodies directly in queries without validation.
Request bodies in API routes should use Zod schemas for validation.
API routes should only return necessary fields using Prisma's 'select' and must not include sensitive data in error messages.
Error messages in API routes must not reveal internal details; use generic errors and SafeError for user-facing errors.
All QStash endpoints (API routes called via publishToQstash or publishToQstashQueue) must use verifySignatureAppRouter to verify request authenticity.
All cron endpoints in API routes must use hasCronSecret or hasPostCronSecret for authentication.
Do not hardcode weak or plaintext secrets in API route files; secrets must not be directly assigned as string literals.
Review all new withError usage in API routes to ensure custom authentication is implemented where required.
Files:
apps/web/app/api/google/webhook/process-label-removed-event.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useelements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...
Files:
apps/web/app/api/google/webhook/process-label-removed-event.ts
!pages/_document.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)
!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.
Files:
apps/web/app/api/google/webhook/process-label-removed-event.ts
🧬 Code Graph Analysis (1)
apps/web/app/api/google/webhook/process-label-removed-event.ts (4)
apps/web/utils/gmail/label.ts (1)
GmailLabel(20-34)apps/web/utils/llms/types.ts (1)
EmailAccountWithAI(10-29)apps/web/app/api/outlook/webhook/logger.ts (1)
logger(3-3)apps/web/utils/email.ts (1)
extractEmailAddress(19-52)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Static Code Analysis Js
- GitHub Check: Jit Security
🔇 Additional comments (1)
apps/web/app/api/google/webhook/process-label-removed-event.ts (1)
13-22: Confirm whether Gmail CATEGORY_ labels should be treated as system labels*If you don’t intend to learn from auto-categorization (Promotions/Social/etc.), add them here. Otherwise, removal of category labels will trigger learning.
If desired, apply:
const SYSTEM_LABELS = [ GmailLabel.INBOX, GmailLabel.SENT, GmailLabel.DRAFT, GmailLabel.SPAM, GmailLabel.TRASH, GmailLabel.IMPORTANT, GmailLabel.STARRED, GmailLabel.UNREAD, + GmailLabel.PERSONAL, + GmailLabel.SOCIAL, + GmailLabel.PROMOTIONS, + GmailLabel.FORUMS, + GmailLabel.UPDATES, ];
| return; | ||
| } | ||
|
|
||
| if (labelName === inboxZeroLabels.cold_email.name) { |
There was a problem hiding this comment.
Only handling Cold email tag removal for now.
|
@claude check this |
|
@claude review |
Summary by CodeRabbit