Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Caution Review failedThe pull request is closed. Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughRefactors sender-pattern analysis to use EmailProvider (provider-based message/thread access), adds request-scoped Logger propagation, introduces thread-level gating and sender-rule history checks, passes historical rule info into AI detection, and conditionally persists learned patterns. Adds provider mocks, types, tests, docs, and bumps version. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Client
participant Route as analyze-sender-pattern route
participant Provider as EmailProvider
participant History as checkSenderRuleHistory
participant AI as aiDetectRecurringPattern
participant DB as DB (persist learned pattern)
Note over Route,Provider: Request-scoped Logger threaded through calls
Client->>Route: POST /api/ai/analyze-sender-pattern
Route->>Provider: getThreadsWithQuery / getThreadMessages
Provider-->>Route: threadsWithMessages
alt threads < THRESHOLD_THREADS
Route-->>Client: 200 {"status":"skipped","reason":"not_enough_threads"}
else sufficient threads
Route->>History: checkSenderRuleHistory(emailAccountId, from, provider)
History->>Provider: getMessagesFromSender(...)
Provider-->>History: messages
History->>DB: query executedRules for message IDs
DB-->>History: executed rules summary
History-->>Route: SenderRuleHistory (hasConsistentRule?, consistentRuleName?)
alt no consistent history
Route-->>Client: 200 {"status":"skipped","reason":"no_consistent_history"}
else consistent history
Route->>AI: aiDetectRecurringPattern(allMessages, consistentRuleName)
AI-->>Route: patternResult (matchedRule, confidence)
alt matchedRule == consistentRuleName
Route->>DB: persist learned pattern
DB-->>Route: saved
else mismatch
Route-->>Route: log warning (skip persist)
end
Route-->>Client: 200 patternResult
end
end
sequenceDiagram
autonumber
participant Choose as choose-rule flow
participant Helper as shouldAnalyzeSenderPattern
participant Analyzer as analyzeSenderPatternIfAiMatch
participant API as analyze-sender-pattern route
Choose->>Analyzer: candidate result
Analyzer->>Helper: shouldAnalyzeSenderPattern({isTest, result})?
alt false
Analyzer-->>Choose: skip AI analysis
else true
Analyzer->>Analyzer: extract from-address
alt from exists
Analyzer->>API: trigger analyze-sender-pattern with provider & logger
API-->>Analyzer: response/result
else
Analyzer-->>Choose: skip (no address)
end
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro 📒 Files selected for processing (3)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (3)
apps/web/utils/ai/choose-rule/run-rules.ts (1)
329-352: Consider excluding PRESET matches from learning as wellIf PRESET matches are “static” (non‑AI) like the others, include them in the skip list to avoid unnecessary learning.
Apply this small diff:
if ( result.matchReasons?.some( (reason) => reason.type === "STATIC" || reason.type === "GROUP" || - reason.type === "CATEGORY", + reason.type === "CATEGORY" || + reason.type === "PRESET", ) ) return false;Optionally, add a focused unit test around
shouldAnalyzeSenderPatterncovering TO_REPLY, STATIC/GROUP/CATEGORY/PRESET, and isTest=true cases.apps/web/app/api/ai/analyze-sender-pattern/route.ts (2)
18-18: Make threshold configurable (optional)Hard‑coding 5 is fine for now; consider an env/config knob to tune without a deploy.
-const THRESHOLD_THREADS = 5; +const THRESHOLD_THREADS = Number(process.env.AI_SENDER_MIN_THREADS ?? 5);
121-126: Fix log message: we’re gating by threads, not emailsUpdate the log string (and optionally the field name) to avoid confusion when analyzing metrics.
- logger.info("Not enough emails found from this sender", { + logger.info("Not enough threads found from this sender", { from, emailAccountId, - count: threadsWithMessages.length, + threadCount: threadsWithMessages.length, });
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
apps/web/app/api/ai/analyze-sender-pattern/route.ts(2 hunks)apps/web/utils/ai/choose-rule/run-rules.ts(2 hunks)version.txt(1 hunks)
🧰 Additional context used
📓 Path-based instructions (14)
!{.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:
version.txtapps/web/app/api/ai/analyze-sender-pattern/route.tsapps/web/utils/ai/choose-rule/run-rules.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:
version.txtapps/web/app/api/ai/analyze-sender-pattern/route.tsapps/web/utils/ai/choose-rule/run-rules.ts
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/ai/analyze-sender-pattern/route.tsapps/web/utils/ai/choose-rule/run-rules.ts
apps/web/app/**
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
NextJS app router structure with (app) directory
Files:
apps/web/app/api/ai/analyze-sender-pattern/route.ts
apps/web/app/api/**/route.ts
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/app/api/**/route.ts: UsewithAuthfor user-level operations
UsewithEmailAccountfor email-account-level operations
Do NOT use POST API routes for mutations - use server actions instead
No need for try/catch in GET routes when using middleware
Export response types from GET routes
apps/web/app/api/**/route.ts: Wrap all GET API route handlers withwithAuthorwithEmailAccountmiddleware for authentication and authorization.
Export response types from GET API routes for type-safe client usage.
Do not use try/catch in GET API routes when using authentication middleware; rely on centralized error handling.
Files:
apps/web/app/api/ai/analyze-sender-pattern/route.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/ai/analyze-sender-pattern/route.tsapps/web/utils/ai/choose-rule/run-rules.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/ai/analyze-sender-pattern/route.tsapps/web/utils/ai/choose-rule/run-rules.ts
**/api/**/route.ts
📄 CodeRabbit inference engine (.cursor/rules/security.mdc)
**/api/**/route.ts: ALL API routes that handle user data MUST use appropriate authentication and authorization middleware (withAuth or withEmailAccount).
ALL database queries in API routes MUST be scoped to the authenticated user/account (e.g., include userId or emailAccountId in query filters).
Always validate that resources belong to the authenticated user before performing operations (resource ownership validation).
UsewithEmailAccountmiddleware for API routes that operate on a specific email account (i.e., use or requireemailAccountId).
UsewithAuthmiddleware for API routes that operate at the user level (i.e., use or require onlyuserId).
UsewithErrormiddleware (with proper validation) for public endpoints, custom authentication, or cron endpoints.
Cron endpoints MUST usewithErrormiddleware and validate the cron secret usinghasCronSecret(request)orhasPostCronSecret(request).
Cron endpoints MUST capture unauthorized attempts withcaptureExceptionand return a 401 status for unauthorized requests.
All parameters in API routes MUST be validated for type, format, and length before use.
Request bodies in API routes MUST be validated using Zod schemas before use.
All Prisma queries in API routes MUST only return necessary fields and never expose sensitive data.
Error messages in API routes MUST not leak internal information or sensitive data; use generic error messages and SafeError where appropriate.
API routes MUST use a consistent error response format, returning JSON with an error message and status code.
AllfindUniqueandfindFirstPrisma calls in API routes MUST include ownership filters (e.g., userId or emailAccountId).
AllfindManyPrisma calls in API routes MUST be scoped to the authenticated user's data.
Never use direct object references in API routes without ownership checks (prevent IDOR vulnerabilities).
Prevent mass assignment vulnerabilities by only allowing explicitly whitelisted fields in update operations in AP...
Files:
apps/web/app/api/ai/analyze-sender-pattern/route.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/ai/analyze-sender-pattern/route.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/ai/analyze-sender-pattern/route.tsapps/web/utils/ai/choose-rule/run-rules.ts
apps/web/utils/**
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Create utility functions in
utils/folder for reusable logic
Files:
apps/web/utils/ai/choose-rule/run-rules.ts
apps/web/utils/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
apps/web/utils/**/*.ts: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size
Files:
apps/web/utils/ai/choose-rule/run-rules.ts
apps/web/utils/ai/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/llm.mdc)
apps/web/utils/ai/**/*.{ts,tsx}: Place main LLM feature implementations under apps/web/utils/ai/
LLM feature functions should follow the provided TypeScript pattern (separate system/user prompts, use createGenerateObject, Zod schema validation, early validation, return result.object)
Keep system prompts and user prompts separate
System prompt should define the LLM's role and task specifications
User prompt should contain the actual data and context
Always define a Zod schema for response validation
Make Zod schemas as specific as possible to guide LLM output
Use descriptive scoped loggers for each feature
Log inputs and outputs with appropriate log levels and include relevant context
Implement early returns for invalid inputs
Use proper error types and logging for failures
Implement fallbacks for AI failures
Add retry logic for transient failures using withRetry
Use XML-like tags to structure data in prompts
Remove excessive whitespace and truncate long inputs in prompts
Format prompt data consistently across similar functions
Use TypeScript types for all parameters and return values in LLM features
Define clear interfaces for complex input/output structures in LLM features
Files:
apps/web/utils/ai/choose-rule/run-rules.ts
apps/web/utils/{ai,llms}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/llm.mdc)
Keep related AI functions co-located and extract common patterns into utilities; document complex AI logic with clear comments
Files:
apps/web/utils/ai/choose-rule/run-rules.ts
🧠 Learnings (2)
📚 Learning: 2025-09-17T22:05:28.616Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-09-17T22:05:28.616Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.{ts,tsx} : Keep related AI functions co-located and extract common patterns into utilities; document complex AI logic with clear comments
Applied to files:
apps/web/utils/ai/choose-rule/run-rules.ts
📚 Learning: 2025-08-31T12:17:19.419Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-08-31T12:17:19.419Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Test both AI and non-AI code paths (e.g., return unchanged when no AI processing is needed)
Applied to files:
apps/web/utils/ai/choose-rule/run-rules.ts
🧬 Code graph analysis (1)
apps/web/utils/ai/choose-rule/run-rules.ts (1)
apps/web/utils/ai/choose-rule/types.ts (1)
MatchReason(29-34)
⏰ 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). (1)
- GitHub Check: cubic · AI code reviewer
🔇 Additional comments (3)
version.txt (1)
1-1: Version bump to v2.10.3 looks goodNo issues. Please ensure tag/changelog are updated alongside the release process.
apps/web/app/api/ai/analyze-sender-pattern/route.ts (1)
131-134: LGTM: defer flattening until after threshold checkThis reduces work when under threshold and keeps the flow clear.
apps/web/utils/ai/choose-rule/run-rules.ts (1)
316-327: Approve — runRules is only used server-side; after() usage is safe.Call sites verified: apps/web/utils/actions/ai-rule.ts (starts with "use server"), apps/web/app/api/outlook/webhook/process-history-item.ts, apps/web/app/api/google/webhook/process-history-item.ts. Client components only import types; tests mock next/server.after.
There was a problem hiding this comment.
1 issue found across 3 files
Prompt for AI agents (all 1 issues)
Understand the root cause of the following 1 issues and fix them.
<file name="apps/web/app/api/ai/analyze-sender-pattern/route.ts">
<violation number="1" location="apps/web/app/api/ai/analyze-sender-pattern/route.ts:125">
Logged count now represents threads, but the message says "emails"; this can mislead logs/analytics. Log the number of emails or rename fields/message to reflect threads.</violation>
</file>
React with 👍 or 👎 to teach cubic. Mention @cubic-dev-ai to give feedback, ask questions, or re-run the review.
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
apps/web/utils/ai/choose-rule/ai-detect-recurring-pattern.ts (2)
13-16: Tighten schema: prefernullable()overnullish().We expect either a string or null; allowing
undefinedweakens validation.-const schema = z.object({ - matchedRule: z.string().nullish(), +const schema = z.object({ + matchedRule: z.string().nullable(), explanation: z.string(), });
33-37: Guard against empty emails array before accessingemails[0].This will throw when
emailsis empty.- // Extract the sender email from the first email - // All emails should be from the same sender - const senderEmail = emails[0].from; + // Extract the sender email from the first email + // All emails should be from the same sender + if (!emails.length || !emails[0]?.from) return null; + const senderEmail = emails[0].from;
🧹 Nitpick comments (3)
.cursor/rules/testing.mdc (1)
39-45: Usetsfence, nottsx, in docs import example.The snippet is a plain TypeScript import; prefer
tsfor accuracy.-```tsx +```ts import { getEmail, getEmailAccount, getRule } from "@/__tests__/helpers";apps/web/utils/ai/choose-rule/ai-detect-recurring-pattern.ts (1)
44-54: SanitizeconsistentRuleNamein prompt interpolation.If rule names contain quotes/newlines, they can degrade prompt formatting. Escape minimal characters.
-${consistentRuleName ? `IMPORTANT: Historical data shows that ALL previous emails from this sender have been matched to the "${consistentRuleName}" rule. Your task is to verify if this pattern should be learned for future emails.` : ""} +${consistentRuleName ? (() => { + const safe = consistentRuleName.replace(/["\\]/g, "\\$&").replace(/\n/g, " "); + return `IMPORTANT: Historical data shows that ALL previous emails from this sender have been matched to the "${safe}" rule. Your task is to verify if this pattern should be learned for future emails.`; +})() : ""} @@ -${consistentRuleName ? `- The content justifies always matching to the "${consistentRuleName}" rule` : ""} +${consistentRuleName ? (() => { + const safe = consistentRuleName.replace(/["\\]/g, "\\$&").replace(/\n/g, " "); + return `- The content justifies always matching to the "${safe}" rule`; +})() : ""}apps/web/utils/rule/check-sender-rule-history.ts (1)
71-93: Optional: count per-message multi-rule cases explicitly to avoid masking conflicts.If a message had multiple rules applied, current dedupe by
messageIdwill undercount and may misclassify consistency.I can provide a follow-up patch to aggregate
messageId -> Set<ruleId>and compute consistency only when each set size is 1 and the union size is 1.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (11)
.cursor/rules/testing.mdc(1 hunks)apps/web/__tests__/ai-choose-rule.test.ts(2 hunks)apps/web/__tests__/helpers.ts(3 hunks)apps/web/app/api/ai/analyze-sender-pattern/route.ts(5 hunks)apps/web/utils/__mocks__/email-provider.ts(1 hunks)apps/web/utils/ai/choose-rule/ai-detect-recurring-pattern.ts(2 hunks)apps/web/utils/email/google.ts(1 hunks)apps/web/utils/email/microsoft.ts(1 hunks)apps/web/utils/email/types.ts(1 hunks)apps/web/utils/rule/check-sender-rule-history.test.ts(1 hunks)apps/web/utils/rule/check-sender-rule-history.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (22)
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/utils/email/types.tsapps/web/utils/__mocks__/email-provider.tsapps/web/utils/rule/check-sender-rule-history.tsapps/web/utils/rule/check-sender-rule-history.test.tsapps/web/utils/email/google.tsapps/web/utils/email/microsoft.tsapps/web/app/api/ai/analyze-sender-pattern/route.tsapps/web/__tests__/ai-choose-rule.test.tsapps/web/__tests__/helpers.tsapps/web/utils/ai/choose-rule/ai-detect-recurring-pattern.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/utils/email/types.tsapps/web/utils/__mocks__/email-provider.tsapps/web/utils/rule/check-sender-rule-history.ts.cursor/rules/testing.mdcapps/web/utils/rule/check-sender-rule-history.test.tsapps/web/utils/email/google.tsapps/web/utils/email/microsoft.tsapps/web/app/api/ai/analyze-sender-pattern/route.tsapps/web/__tests__/ai-choose-rule.test.tsapps/web/__tests__/helpers.tsapps/web/utils/ai/choose-rule/ai-detect-recurring-pattern.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/utils/email/types.tsapps/web/utils/__mocks__/email-provider.tsapps/web/utils/rule/check-sender-rule-history.tsapps/web/utils/rule/check-sender-rule-history.test.tsapps/web/utils/email/google.tsapps/web/utils/email/microsoft.tsapps/web/app/api/ai/analyze-sender-pattern/route.tsapps/web/__tests__/ai-choose-rule.test.tsapps/web/__tests__/helpers.tsapps/web/utils/ai/choose-rule/ai-detect-recurring-pattern.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/utils/email/types.tsapps/web/utils/__mocks__/email-provider.tsapps/web/utils/rule/check-sender-rule-history.tsapps/web/utils/rule/check-sender-rule-history.test.tsapps/web/utils/email/google.tsapps/web/utils/email/microsoft.tsapps/web/app/api/ai/analyze-sender-pattern/route.tsapps/web/__tests__/ai-choose-rule.test.tsapps/web/__tests__/helpers.tsapps/web/utils/ai/choose-rule/ai-detect-recurring-pattern.ts
apps/web/utils/**
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Create utility functions in
utils/folder for reusable logic
Files:
apps/web/utils/email/types.tsapps/web/utils/__mocks__/email-provider.tsapps/web/utils/rule/check-sender-rule-history.tsapps/web/utils/rule/check-sender-rule-history.test.tsapps/web/utils/email/google.tsapps/web/utils/email/microsoft.tsapps/web/utils/ai/choose-rule/ai-detect-recurring-pattern.ts
apps/web/utils/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
apps/web/utils/**/*.ts: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size
Files:
apps/web/utils/email/types.tsapps/web/utils/__mocks__/email-provider.tsapps/web/utils/rule/check-sender-rule-history.tsapps/web/utils/rule/check-sender-rule-history.test.tsapps/web/utils/email/google.tsapps/web/utils/email/microsoft.tsapps/web/utils/ai/choose-rule/ai-detect-recurring-pattern.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/utils/email/types.tsapps/web/utils/__mocks__/email-provider.tsapps/web/utils/rule/check-sender-rule-history.tsapps/web/utils/rule/check-sender-rule-history.test.tsapps/web/utils/email/google.tsapps/web/utils/email/microsoft.tsapps/web/app/api/ai/analyze-sender-pattern/route.tsapps/web/__tests__/ai-choose-rule.test.tsapps/web/__tests__/helpers.tsapps/web/utils/ai/choose-rule/ai-detect-recurring-pattern.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/utils/email/types.tsapps/web/utils/__mocks__/email-provider.tsapps/web/utils/rule/check-sender-rule-history.ts.cursor/rules/testing.mdcapps/web/utils/rule/check-sender-rule-history.test.tsapps/web/utils/email/google.tsapps/web/utils/email/microsoft.tsapps/web/app/api/ai/analyze-sender-pattern/route.tsapps/web/__tests__/ai-choose-rule.test.tsapps/web/__tests__/helpers.tsapps/web/utils/ai/choose-rule/ai-detect-recurring-pattern.ts
.cursor/rules/*.mdc
📄 CodeRabbit inference engine (.cursor/rules/cursor-rules.mdc)
.cursor/rules/*.mdc: Always place rule files in PROJECT_ROOT/.cursor/rules/
Follow the naming convention: use kebab-case for filenames, always use .mdc extension, and make names descriptive of the rule's purpose
Cursor rules must have a frontmatter section with description, globs, and alwaysApply fields, followed by markdown content explaining the rule
Files:
.cursor/rules/testing.mdc
**/*.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/utils/rule/check-sender-rule-history.test.tsapps/web/__tests__/ai-choose-rule.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/utils/rule/check-sender-rule-history.test.tsapps/web/__tests__/ai-choose-rule.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/utils/rule/check-sender-rule-history.test.tsapps/web/__tests__/ai-choose-rule.test.ts
apps/web/app/**
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
NextJS app router structure with (app) directory
Files:
apps/web/app/api/ai/analyze-sender-pattern/route.ts
apps/web/app/api/**/route.ts
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/app/api/**/route.ts: UsewithAuthfor user-level operations
UsewithEmailAccountfor email-account-level operations
Do NOT use POST API routes for mutations - use server actions instead
No need for try/catch in GET routes when using middleware
Export response types from GET routes
apps/web/app/api/**/route.ts: Wrap all GET API route handlers withwithAuthorwithEmailAccountmiddleware for authentication and authorization.
Export response types from GET API routes for type-safe client usage.
Do not use try/catch in GET API routes when using authentication middleware; rely on centralized error handling.
Files:
apps/web/app/api/ai/analyze-sender-pattern/route.ts
**/api/**/route.ts
📄 CodeRabbit inference engine (.cursor/rules/security.mdc)
**/api/**/route.ts: ALL API routes that handle user data MUST use appropriate authentication and authorization middleware (withAuth or withEmailAccount).
ALL database queries in API routes MUST be scoped to the authenticated user/account (e.g., include userId or emailAccountId in query filters).
Always validate that resources belong to the authenticated user before performing operations (resource ownership validation).
UsewithEmailAccountmiddleware for API routes that operate on a specific email account (i.e., use or requireemailAccountId).
UsewithAuthmiddleware for API routes that operate at the user level (i.e., use or require onlyuserId).
UsewithErrormiddleware (with proper validation) for public endpoints, custom authentication, or cron endpoints.
Cron endpoints MUST usewithErrormiddleware and validate the cron secret usinghasCronSecret(request)orhasPostCronSecret(request).
Cron endpoints MUST capture unauthorized attempts withcaptureExceptionand return a 401 status for unauthorized requests.
All parameters in API routes MUST be validated for type, format, and length before use.
Request bodies in API routes MUST be validated using Zod schemas before use.
All Prisma queries in API routes MUST only return necessary fields and never expose sensitive data.
Error messages in API routes MUST not leak internal information or sensitive data; use generic error messages and SafeError where appropriate.
API routes MUST use a consistent error response format, returning JSON with an error message and status code.
AllfindUniqueandfindFirstPrisma calls in API routes MUST include ownership filters (e.g., userId or emailAccountId).
AllfindManyPrisma calls in API routes MUST be scoped to the authenticated user's data.
Never use direct object references in API routes without ownership checks (prevent IDOR vulnerabilities).
Prevent mass assignment vulnerabilities by only allowing explicitly whitelisted fields in update operations in AP...
Files:
apps/web/app/api/ai/analyze-sender-pattern/route.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/ai/analyze-sender-pattern/route.ts
**/__tests__/**/*.{ts,js,tsx,jsx}
📄 CodeRabbit inference engine (.cursor/rules/testing.mdc)
AI tests are placed in the
__tests__directory and are not run by default (they use a real LLM)
Files:
apps/web/__tests__/ai-choose-rule.test.tsapps/web/__tests__/helpers.ts
apps/web/__tests__/**
📄 CodeRabbit inference engine (.cursor/rules/llm-test.mdc)
Place all LLM-related tests under apps/web/tests/
Files:
apps/web/__tests__/ai-choose-rule.test.tsapps/web/__tests__/helpers.ts
apps/web/__tests__/**/*.test.ts
📄 CodeRabbit inference engine (.cursor/rules/llm-test.mdc)
apps/web/__tests__/**/*.test.ts: Guard LLM tests with describe.runIf(process.env.RUN_AI_TESTS === "true") so they only run when RUN_AI_TESTS="true"
Mock the "server-only" module in LLM tests with vi.mock("server-only", () => ({}))
Clear mocks in a beforeEach using vi.clearAllMocks()
Set and use a generous timeout (e.g., const TIMEOUT = 15_000) for LLM tests and pass it to long-running tests/describe
Create helper functions for common test data (e.g., getUser, getTestData(overrides))
Include standard cases: happy path, error handling, edge cases (empty/null), different user configs, and various input formats
Use console.debug to log generated content for inspection
Do not mock the LLM call; invoke the real LLM in these tests
Test both AI and non-AI code paths (e.g., return unchanged when no AI processing is needed)
Files:
apps/web/__tests__/ai-choose-rule.test.ts
apps/web/__tests__/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/llm.mdc)
Place LLM-specific tests under apps/web/tests/
Files:
apps/web/__tests__/ai-choose-rule.test.tsapps/web/__tests__/helpers.ts
apps/web/utils/ai/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/llm.mdc)
apps/web/utils/ai/**/*.{ts,tsx}: Place main LLM feature implementations under apps/web/utils/ai/
LLM feature functions should follow the provided TypeScript pattern (separate system/user prompts, use createGenerateObject, Zod schema validation, early validation, return result.object)
Keep system prompts and user prompts separate
System prompt should define the LLM's role and task specifications
User prompt should contain the actual data and context
Always define a Zod schema for response validation
Make Zod schemas as specific as possible to guide LLM output
Use descriptive scoped loggers for each feature
Log inputs and outputs with appropriate log levels and include relevant context
Implement early returns for invalid inputs
Use proper error types and logging for failures
Implement fallbacks for AI failures
Add retry logic for transient failures using withRetry
Use XML-like tags to structure data in prompts
Remove excessive whitespace and truncate long inputs in prompts
Format prompt data consistently across similar functions
Use TypeScript types for all parameters and return values in LLM features
Define clear interfaces for complex input/output structures in LLM features
Files:
apps/web/utils/ai/choose-rule/ai-detect-recurring-pattern.ts
apps/web/utils/{ai,llms}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/llm.mdc)
Keep related AI functions co-located and extract common patterns into utilities; document complex AI logic with clear comments
Files:
apps/web/utils/ai/choose-rule/ai-detect-recurring-pattern.ts
🧠 Learnings (13)
📚 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/utils/__mocks__/email-provider.ts
📚 Learning: 2025-08-31T12:17:19.419Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-08-31T12:17:19.419Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Create helper functions for common test data (e.g., getUser, getTestData(overrides))
Applied to files:
.cursor/rules/testing.mdcapps/web/__tests__/helpers.ts
📚 Learning: 2025-08-31T12:17:19.419Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-08-31T12:17:19.419Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Include standard cases: happy path, error handling, edge cases (empty/null), different user configs, and various input formats
Applied to files:
.cursor/rules/testing.mdc
📚 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:
.cursor/rules/testing.mdcapps/web/utils/rule/check-sender-rule-history.test.tsapps/web/__tests__/ai-choose-rule.test.ts
📚 Learning: 2025-07-20T09:03:06.318Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-20T09:03:06.318Z
Learning: Applies to **/*.{test,spec}.{js,jsx,ts,tsx} : Make sure the assertion function, like expect, is placed inside an it() function call.
Applied to files:
.cursor/rules/testing.mdc
📚 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/ai/analyze-sender-pattern/route.ts
📚 Learning: 2025-07-08T13:14:07.449Z
Learnt from: elie222
PR: elie222/inbox-zero#537
File: apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx:30-34
Timestamp: 2025-07-08T13:14:07.449Z
Learning: The clean onboarding page in apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx is intentionally Gmail-specific and should show an error for non-Google email accounts rather than attempting to support multiple providers.
Applied to files:
apps/web/app/api/ai/analyze-sender-pattern/route.ts
📚 Learning: 2025-08-31T12:17:19.419Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-08-31T12:17:19.419Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Test both AI and non-AI code paths (e.g., return unchanged when no AI processing is needed)
Applied to files:
apps/web/__tests__/ai-choose-rule.test.tsapps/web/__tests__/helpers.ts
📚 Learning: 2025-09-17T22:05:28.616Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-09-17T22:05:28.616Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.{ts,tsx} : Keep related AI functions co-located and extract common patterns into utilities; document complex AI logic with clear comments
Applied to files:
apps/web/__tests__/ai-choose-rule.test.ts
📚 Learning: 2025-07-18T15:05:16.146Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.146Z
Learning: Applies to apps/web/utils/actions/**/*.ts : Use server actions (with `next-safe-action`) for all mutations (create/update/delete operations); do NOT use POST API routes for mutations.
Applied to files:
apps/web/__tests__/ai-choose-rule.test.ts
📚 Learning: 2025-08-31T12:17:19.419Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-08-31T12:17:19.419Z
Learning: Run AI tests using: pnpm test-ai <test-name>
Applied to files:
apps/web/__tests__/ai-choose-rule.test.ts
📚 Learning: 2025-08-31T12:17:19.419Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-08-31T12:17:19.419Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Guard LLM tests with describe.runIf(process.env.RUN_AI_TESTS === "true") so they only run when RUN_AI_TESTS="true"
Applied to files:
apps/web/__tests__/ai-choose-rule.test.ts
📚 Learning: 2025-07-18T17:27:58.249Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-07-18T17:27:58.249Z
Learning: Applies to apps/web/utils/actions/*.ts : Use `actionClient` when both authenticated user context and a specific `emailAccountId` are needed. The `emailAccountId` must be bound when calling the action from the client.
Applied to files:
apps/web/__tests__/helpers.ts
🧬 Code graph analysis (8)
apps/web/utils/email/types.ts (1)
apps/web/utils/types.ts (1)
ParsedMessage(55-70)
apps/web/utils/__mocks__/email-provider.ts (1)
apps/web/utils/email/types.ts (1)
EmailProvider(36-183)
apps/web/utils/rule/check-sender-rule-history.ts (3)
apps/web/utils/email/types.ts (1)
EmailProvider(36-183)apps/web/utils/logger.ts (1)
createScopedLogger(17-67)apps/web/utils/email.ts (1)
extractEmailAddress(19-52)
apps/web/utils/rule/check-sender-rule-history.test.ts (3)
apps/web/utils/__mocks__/email-provider.ts (1)
createMockEmailProvider(35-118)apps/web/utils/rule/check-sender-rule-history.ts (1)
checkSenderRuleHistory(19-126)apps/web/__tests__/helpers.ts (2)
getMockMessage(66-106)getMockExecutedRule(108-135)
apps/web/utils/email/google.ts (1)
apps/web/utils/types.ts (1)
ParsedMessage(55-70)
apps/web/utils/email/microsoft.ts (1)
apps/web/utils/types.ts (1)
ParsedMessage(55-70)
apps/web/app/api/ai/analyze-sender-pattern/route.ts (4)
apps/web/utils/email/provider.ts (1)
createEmailProvider(13-28)apps/web/utils/rule/check-sender-rule-history.ts (1)
checkSenderRuleHistory(19-126)apps/web/utils/email/types.ts (1)
EmailProvider(36-183)apps/web/utils/email.ts (1)
extractEmailAddress(19-52)
apps/web/utils/ai/choose-rule/ai-detect-recurring-pattern.ts (2)
apps/web/utils/types.ts (1)
EmailForLLM(117-131)apps/web/utils/llms/types.ts (1)
EmailAccountWithAI(10-29)
🔇 Additional comments (30)
apps/web/utils/email/types.ts (1)
124-133: Interface addition looks good and matches provider implementations.Signature is clear and consistent with existing pagination method.
apps/web/__tests__/ai-choose-rule.test.ts (3)
3-3: Enum import usage is fine in tests.Importing
ActionTypefor fixture data here is pragmatic and low-risk.
5-5: Good move to shared helpers.Using
@/__tests__/helperskeeps tests consistent.
82-82: AddingfolderId: nullmaintains parity with model shape.Prevents type drift in action payloads.
apps/web/__tests__/helpers.ts (5)
3-4: LGTM!Import statements are correctly structured and typed. The Prisma types are imported properly for use in the helper functions.
19-21: Add the account provider field to match the updated structure.The new mock object includes an
account.providerfield that's now part of the EmailAccountWithAI structure used in the test infrastructure.
44-64: LGTM - Well-structured test helper for rules.The
getRulehelper provides a comprehensive test fixture with sensible defaults for all required rule fields, following the established pattern of test helpers in this file.
66-106: LGTM - Comprehensive message mock helper.The function provides a complete mock structure for email messages with proper typing and reasonable defaults. Good use of destructuring for optional parameters.
108-135: LGTM - Proper Prisma type integration.The helper correctly uses the Prisma GetPayload type to ensure the mock matches the exact structure expected by the database queries, with proper nested selection types.
apps/web/app/api/ai/analyze-sender-pattern/route.ts (11)
12-14: LGTM - Good provider abstraction imports.The imports properly abstract email provider functionality and introduce the sender rule history checking capability.
18-18: LGTM - More restrictive threshold with clear naming.Changing from email-based to thread-based threshold with a higher bar (3 threads vs previous email threshold) helps ensure pattern detection works on substantial data. The variable name
THRESHOLD_THREADSclearly indicates the unit being measured.
87-95: LGTM - Provider abstraction implementation.The transition to using
createEmailProviderwith the account provider field properly abstracts the email service implementation and enables multi-provider support.
97-101: LGTM - Provider-based thread fetching.The function call correctly passes the provider instance to
getThreadsFromSender, implementing the provider abstraction pattern.
114-122: Fix misleading log message to match thread-based counting.The logged count now represents threads, but the message still says "emails", which can mislead logs and analytics.
Apply this fix to log the actual email count:
- logger.info("Not enough emails found from this sender", { + logger.info("Not enough threads found from this sender", { from, emailAccountId, count: threadsWithMessages.length, });Or alternatively, calculate and log the actual email count:
- logger.info("Not enough emails found from this sender", { + const emailCount = threadsWithMessages.reduce((acc, t) => acc + t.messages.length, 0); + logger.info("Not enough emails found from this sender", { from, emailAccountId, - count: threadsWithMessages.length, + count: emailCount, });
128-144: LGTM - Historical rule consistency gating.The integration of sender history checking provides intelligent gating based on past rule applications. The early exit when there's no consistent rule history prevents unnecessary AI processing and ensures learned patterns are only created when there's evidence of consistent behavior.
146-151: LGTM - Informative logging for consistent patterns.The logging correctly captures the consistent rule name and provides useful context for monitoring pattern detection success.
162-162: LGTM - Context-aware AI detection.Passing the
consistentRuleNameto the AI detection function provides valuable historical context to improve pattern matching accuracy.
165-181: LGTM - Validation of AI suggestions against historical data.This validation logic ensures that learned patterns are only persisted when the AI matches the historical rule, preventing inconsistent automation. The warning log for mismatches is appropriate for monitoring and debugging.
237-246: LGTM - Provider-based thread retrieval.The transition to using the provider's
getThreadsWithQuerymethod properly abstracts the email service implementation while maintaining the same functional behavior.
251-252: LGTM - Provider-consistent message fetching.Using
provider.getThreadMessagesmaintains consistency with the provider abstraction throughout the thread processing logic.apps/web/utils/rule/check-sender-rule-history.test.ts (10)
1-12: LGTM - Proper test setup following guidelines.The test file correctly follows the coding guidelines by mocking Prisma using the established pattern, clearing mocks in beforeEach for isolation, and importing from the designated mock locations.
16-35: LGTM - Comprehensive no-messages scenario test.This test case properly validates the function behavior when no messages are found, ensuring correct return values and provider method calls with expected parameters.
37-125: LGTM - Thorough consistent rule scenario testing.This test comprehensively validates the happy path where all messages match the same rule. It correctly verifies the database query structure, including the proper filtering by message IDs, status, and rule enablement status.
127-193: LGTM - Mixed rules scenario well-covered.The test properly validates the case where messages match different rules, ensuring the function correctly identifies the lack of consistency and properly counts rule matches per rule ID.
195-229: LGTM - No executed rules scenario handled.This test case ensures the function handles the scenario where messages exist but no rules were applied to them, correctly returning zero rule matches.
231-266: LGTM - Error handling tests for robustness.Both error handling tests ensure the function properly propagates errors from the provider and database layers, which is important for debugging and monitoring in production.
268-284: LGTM - Email address extraction validation.This test ensures the function properly extracts clean email addresses from complex "From" headers, which is crucial for accurate sender matching.
286-318: LGTM - Edge case for deleted rules.This test covers an important edge case where executed rules exist but their associated rules have been deleted, ensuring the function still operates correctly.
320-365: LGTM - Deduplication logic validation.This test ensures that duplicate executed rule entries (which can happen in practice) don't inflate the rule match counts, demonstrating proper deduplication logic.
367-400: LGTM - Partial coverage scenario tested.This test validates the function's behavior when only some messages have executed rules, ensuring it still correctly identifies consistent patterns when they exist.
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
apps/web/app/api/ai/analyze-sender-pattern/route.ts (3)
27-48: Auth/ownership checks and HTTP method violate API guidelines; prefer server action or withEmailAccount.
- This POST route mutates DB (upserts, learned patterns) but guidelines say not to use POST API routes for mutations; use server actions instead.
- It operates on a specific email account but doesn’t use withEmailAccount (missing ownership checks; internal API key isn’t sufficient for per-account authorization).
Consider either:
- Move logic to a server action and drop this POST route; or
- Gate this handler with withEmailAccount and remove the internal key path (or keep it only for cron/internal with a separate endpoint).
Example sketch (adjust signature per your middleware API):
-import { withError } from "@/utils/middleware"; +import { withEmailAccount } from "@/utils/middleware"; ... -export const POST = withError(async (request) => { +export const POST = withEmailAccount(async (request, { emailAccount }) => { const json = await request.json(); - let logger = createScopedLogger("api/ai/pattern-match"); + let logger = createScopedLogger("api/ai/pattern-match"); - if (!isValidInternalApiKey(await headers(), logger)) { - logger.error("Invalid API key for sender pattern analysis", json); - return NextResponse.json({ error: "Invalid API key" }); - } - const data = schema.parse(json); - const { emailAccountId } = data; + const data = schema.parse(json); + const emailAccountId = emailAccount.id;
37-45: Validate extracted sender email; reject if extraction fails.extractEmailAddress can return an empty string; querying with from="" can fetch far too much. Add a guard.
const data = schema.parse(json); const { emailAccountId } = data; const from = extractEmailAddress(data.from); + if (!from) { + logger.warn("Invalid 'from' value", { raw: data.from }); + return NextResponse.json({ error: "Invalid sender email" }, { status: 400 }); + }
285-292: Don’t select access/refresh tokens you don’t use.Selecting sensitive fields in API routes violates the “only necessary fields” rule and increases risk if logged later.
account: { select: { - provider: true, - access_token: true, - refresh_token: true, - expires_at: true, + provider: true, }, },
🧹 Nitpick comments (6)
apps/web/app/api/ai/analyze-sender-pattern/route.ts (6)
71-73: Background job returns NextResponse values that are ignored; make process return void and early-exit.process is invoked via after(); its NextResponse returns are unused. Prefer void returns and plain early exits for clarity.
- logger.error("Email account not found"); - return NextResponse.json({ success: false }, { status: 404 }); + logger.error("Email account not found"); + return;Apply the same pattern to other early returns inside process.
Also applies to: 85-87, 91-94, 110-114, 116-122, 140-142, 179-180
181-186: Log error message and stack explicitly (Error objects stringify to {}).Current logging loses context.
- logger.error("Error in pattern match API", { error }); + const err = + error instanceof Error + ? { message: error.message, stack: error.stack } + : { message: String(error) }; + logger.error("Error in pattern match API", err);
232-238: Guard against empty sender in provider query.Double-check here too; bail out early if extraction failed.
const from = extractEmailAddress(sender); + if (!from) { + logger.warn("No valid sender email after extraction"); + return []; + }
246-251: Avoid false positives when detecting “conversation” threads.If a message lacks a From header, s === "" will mark as “other sender”. Ignore empty values.
- const hasOtherSenders = senders.some((s) => s !== from); + const hasOtherSenders = senders.some((s) => s && s !== from);
96-100: Handle unsupported provider explicitly; return 422 instead of throwing.createEmailProvider throws on unsupported provider; catching and returning 422 improves diagnosability vs 500.
- const provider = await createEmailProvider({ - emailAccountId, - provider: account.provider, - }); + let provider: EmailProvider; + try { + provider = await createEmailProvider({ + emailAccountId, + provider: account.provider, + }); + } catch (e) { + logger.error("Unsupported email provider", { + provider: account.provider, + }); + return; + }
21-25: Tighten Zod schema per validation guidelines.Add basic bounds to guard against pathological inputs.
-const schema = z.object({ - emailAccountId: z.string(), - from: z.string(), -}); +const schema = z.object({ + emailAccountId: z.string().min(1).max(64), + from: z.string().min(3).max(320), +});
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
apps/web/app/api/ai/analyze-sender-pattern/route.ts(8 hunks)
🧰 Additional context used
📓 Path-based instructions (10)
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/ai/analyze-sender-pattern/route.ts
apps/web/app/**
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
NextJS app router structure with (app) directory
Files:
apps/web/app/api/ai/analyze-sender-pattern/route.ts
apps/web/app/api/**/route.ts
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/app/api/**/route.ts: UsewithAuthfor user-level operations
UsewithEmailAccountfor email-account-level operations
Do NOT use POST API routes for mutations - use server actions instead
No need for try/catch in GET routes when using middleware
Export response types from GET routes
apps/web/app/api/**/route.ts: Wrap all GET API route handlers withwithAuthorwithEmailAccountmiddleware for authentication and authorization.
Export response types from GET API routes for type-safe client usage.
Do not use try/catch in GET API routes when using authentication middleware; rely on centralized error handling.
Files:
apps/web/app/api/ai/analyze-sender-pattern/route.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/ai/analyze-sender-pattern/route.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/ai/analyze-sender-pattern/route.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/ai/analyze-sender-pattern/route.ts
**/api/**/route.ts
📄 CodeRabbit inference engine (.cursor/rules/security.mdc)
**/api/**/route.ts: ALL API routes that handle user data MUST use appropriate authentication and authorization middleware (withAuth or withEmailAccount).
ALL database queries in API routes MUST be scoped to the authenticated user/account (e.g., include userId or emailAccountId in query filters).
Always validate that resources belong to the authenticated user before performing operations (resource ownership validation).
UsewithEmailAccountmiddleware for API routes that operate on a specific email account (i.e., use or requireemailAccountId).
UsewithAuthmiddleware for API routes that operate at the user level (i.e., use or require onlyuserId).
UsewithErrormiddleware (with proper validation) for public endpoints, custom authentication, or cron endpoints.
Cron endpoints MUST usewithErrormiddleware and validate the cron secret usinghasCronSecret(request)orhasPostCronSecret(request).
Cron endpoints MUST capture unauthorized attempts withcaptureExceptionand return a 401 status for unauthorized requests.
All parameters in API routes MUST be validated for type, format, and length before use.
Request bodies in API routes MUST be validated using Zod schemas before use.
All Prisma queries in API routes MUST only return necessary fields and never expose sensitive data.
Error messages in API routes MUST not leak internal information or sensitive data; use generic error messages and SafeError where appropriate.
API routes MUST use a consistent error response format, returning JSON with an error message and status code.
AllfindUniqueandfindFirstPrisma calls in API routes MUST include ownership filters (e.g., userId or emailAccountId).
AllfindManyPrisma calls in API routes MUST be scoped to the authenticated user's data.
Never use direct object references in API routes without ownership checks (prevent IDOR vulnerabilities).
Prevent mass assignment vulnerabilities by only allowing explicitly whitelisted fields in update operations in AP...
Files:
apps/web/app/api/ai/analyze-sender-pattern/route.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/ai/analyze-sender-pattern/route.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/ai/analyze-sender-pattern/route.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/ai/analyze-sender-pattern/route.ts
🧠 Learnings (2)
📚 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/ai/analyze-sender-pattern/route.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/ai/analyze-sender-pattern/route.ts
🧬 Code graph analysis (1)
apps/web/app/api/ai/analyze-sender-pattern/route.ts (6)
apps/web/utils/logger.ts (2)
createScopedLogger(17-67)Logger(5-5)apps/web/utils/email/provider.ts (1)
createEmailProvider(13-28)apps/web/utils/rule/check-sender-rule-history.ts (1)
checkSenderRuleHistory(19-126)apps/web/utils/rule/learned-patterns.ts (1)
saveLearnedPattern(13-67)apps/web/utils/email/types.ts (1)
EmailProvider(36-183)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 (3)
apps/web/app/api/ai/analyze-sender-pattern/route.ts (3)
116-122: Log message says “emails” but count is threads; fix or log both.This repeats the earlier issue noted in a past review. Either rename to “threads” or compute email count.
- if (threadsWithMessages.length < THRESHOLD_THREADS) { - logger.info("Not enough emails found from this sender", { - count: threadsWithMessages.length, - }); - return NextResponse.json({ success: true }); - } + if (threadsWithMessages.length < THRESHOLD_THREADS) { + const emailCount = threadsWithMessages.reduce( + (acc, t) => acc + t.messages.length, + 0, + ); + logger.info("Not enough threads found from this sender", { + threads: threadsWithMessages.length, + emails: emailCount, + threshold: THRESHOLD_THREADS, + }); + return; + }
108-114: Confirm intentional choice to skip savePatternCheck on “no threads” and “below threshold”.If you want to avoid re-triggering this work too often, consider persisting lastAnalyzedAt even when under threshold to back off.
Would you like me to add a cooldown/upsert in these paths?
Also applies to: 116-122
30-46: Good: scoped logger created and contextualized, passed through background task.This aligns with the logging guidelines and avoids global state.
If any downstream function needs per-thread context, prefer logger.with({ threadId }) locally rather than threading IDs through all calls.
Summary by CodeRabbit