Skip to content

Require higher threshold to learn#804

Merged
elie222 merged 7 commits intomainfrom
feat/improve-learning
Sep 20, 2025
Merged

Require higher threshold to learn#804
elie222 merged 7 commits intomainfrom
feat/improve-learning

Conversation

@elie222
Copy link
Owner

@elie222 elie222 commented Sep 19, 2025

Summary by CodeRabbit

  • New Features
    • Provider-based email retrieval, sender-history analysis, thread-level gating, and improved AI pattern detection that ties results to historical consistency.
  • Refactor
    • Request-scoped logging/context propagation and stricter AI gating so learned patterns are only persisted when consistent with history.
  • Tests
    • Expanded mocks and comprehensive tests covering provider flows and sender-rule history scenarios.
  • Documentation
    • Testing docs updated with helper usage examples.
  • Chores
    • Version bumped to v2.10.3.

@vercel
Copy link

vercel bot commented Sep 19, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Updated (UTC)
inbox-zero Ready Ready Preview Sep 20, 2025 6:28pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 19, 2025

Caution

Review failed

The pull request is closed.

Note

Other AI code review bot(s) detected

CodeRabbit 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.

Walkthrough

Refactors 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

Cohort / File(s) Summary
Analyze sender pattern route
apps/web/app/api/ai/analyze-sender-pattern/route.ts
Replace Gmail client usage with EmailProvider flow, create per-request Logger, use THRESHOLD_THREADS, call checkSenderRuleHistory, pass consistentRuleName into AI, conditionally persist learned patterns, and enhance error handling/log fields.
Sender history utility & tests
apps/web/utils/rule/check-sender-rule-history.ts, apps/web/utils/rule/check-sender-rule-history.test.ts
Add SenderRuleHistory interface and checkSenderRuleHistory (fetch messages via provider, dedupe/aggregate executed rules, compute consistentRuleName) with comprehensive tests for multiple scenarios and error paths.
Email provider surface & implementations
apps/web/utils/email/types.ts, apps/web/utils/email/google.ts, apps/web/utils/email/microsoft.ts
Add getMessagesFromSender(...) to EmailProvider interface and implement it for Gmail and Outlook providers (sender-scoped paginated retrieval with before/after filters); Outlook adds OData-safe escaping.
Provider mocks
apps/web/utils/__mocks__/email-provider.ts
Add createMockEmailProvider(overrides?) and export mockGmailProvider / mockOutlookProvider with full EmailProvider method mocks for tests.
AI detection prompt change
apps/web/utils/ai/choose-rule/ai-detect-recurring-pattern.ts
Add optional consistentRuleName?: string parameter and inject history-aware guidance/examples into the system prompt when provided.
Choose-rule refactor
apps/web/utils/ai/choose-rule/run-rules.ts
Extract gating logic into shouldAnalyzeSenderPattern({ isTest, result }) and call it from analyzeSenderPatternIfAiMatch (semantics preserved).
Test helpers & tests
apps/web/__tests__/helpers.ts, apps/web/__tests__/ai-choose-rule.test.ts
Add shared test helpers (getRule, getMockMessage, getMockExecutedRule), update tests to use shared helpers, include folderId: null in action payloads, and remove duplicated local helpers.
Docs / MD cursor
.cursor/rules/testing.mdc
Add "Helpers" section demonstrating import and usage of test helpers.
Version
version.txt
Bump version from v2.10.2 to v2.10.3.

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
Loading
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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

I nibble bytes and hop through threads,
I count old rules where history treads.
A scoped logger hums, the provider sings,
AI learns ties from remembered things.
v2.10.3 — carrots, logs, and cozy strings. 🐇✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "Require higher threshold to learn" succinctly and accurately captures the PR’s primary change—raising the gating threshold for learning sender patterns (e.g., introducing THRESHOLD_THREADS and history-based gating in analyze-sender-pattern); it is concise, relevant, and clear for a teammate scanning the history.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 25633dd and 9c71c1f.

📒 Files selected for processing (3)
  • apps/web/app/api/ai/analyze-sender-pattern/route.ts (8 hunks)
  • apps/web/utils/__mocks__/email-provider.ts (1 hunks)
  • apps/web/utils/email/microsoft.ts (5 hunks)

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 well

If 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 shouldAnalyzeSenderPattern covering 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 emails

Update 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

📥 Commits

Reviewing files that changed from the base of the PR and between 628c6b2 and a88eede.

📒 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.txt
  • apps/web/app/api/ai/analyze-sender-pattern/route.ts
  • apps/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.txt
  • apps/web/app/api/ai/analyze-sender-pattern/route.ts
  • apps/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.ts
  • apps/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: Use withAuth for user-level operations
Use withEmailAccount for 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 with withAuth or withEmailAccount middleware 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.ts
  • apps/web/utils/ai/choose-rule/run-rules.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)

**/*.{ts,tsx}: Use createScopedLogger for logging in backend TypeScript files
Typically add the logger initialization at the top of the file when using createScopedLogger
Only use .with() on a logger instance within a specific function, not for a global logger

Import 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
  • apps/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).
Use withEmailAccount middleware for API routes that operate on a specific email account (i.e., use or require emailAccountId).
Use withAuth middleware for API routes that operate at the user level (i.e., use or require only userId).
Use withError middleware (with proper validation) for public endpoints, custom authentication, or cron endpoints.
Cron endpoints MUST use withError middleware and validate the cron secret using hasCronSecret(request) or hasPostCronSecret(request).
Cron endpoints MUST capture unauthorized attempts with captureException and 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.
All findUnique and findFirst Prisma calls in API routes MUST include ownership filters (e.g., userId or emailAccountId).
All findMany Prisma 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 use elements 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
  • apps/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 good

No 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 check

This 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.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 &quot;emails&quot;; 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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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: prefer nullable() over nullish().

We expect either a string or null; allowing undefined weakens 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 accessing emails[0].

This will throw when emails is 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: Use ts fence, not tsx, in docs import example.

The snippet is a plain TypeScript import; prefer ts for accuracy.

-```tsx
+```ts
 import { getEmail, getEmailAccount, getRule } from "@/__tests__/helpers";
apps/web/utils/ai/choose-rule/ai-detect-recurring-pattern.ts (1)

44-54: Sanitize consistentRuleName in 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 messageId will 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

📥 Commits

Reviewing files that changed from the base of the PR and between a88eede and 3b85408.

📒 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.ts
  • apps/web/utils/__mocks__/email-provider.ts
  • apps/web/utils/rule/check-sender-rule-history.ts
  • apps/web/utils/rule/check-sender-rule-history.test.ts
  • apps/web/utils/email/google.ts
  • apps/web/utils/email/microsoft.ts
  • apps/web/app/api/ai/analyze-sender-pattern/route.ts
  • apps/web/__tests__/ai-choose-rule.test.ts
  • apps/web/__tests__/helpers.ts
  • apps/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.ts
  • apps/web/utils/__mocks__/email-provider.ts
  • apps/web/utils/rule/check-sender-rule-history.ts
  • .cursor/rules/testing.mdc
  • apps/web/utils/rule/check-sender-rule-history.test.ts
  • apps/web/utils/email/google.ts
  • apps/web/utils/email/microsoft.ts
  • apps/web/app/api/ai/analyze-sender-pattern/route.ts
  • apps/web/__tests__/ai-choose-rule.test.ts
  • apps/web/__tests__/helpers.ts
  • apps/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.ts
  • apps/web/utils/__mocks__/email-provider.ts
  • apps/web/utils/rule/check-sender-rule-history.ts
  • apps/web/utils/rule/check-sender-rule-history.test.ts
  • apps/web/utils/email/google.ts
  • apps/web/utils/email/microsoft.ts
  • apps/web/app/api/ai/analyze-sender-pattern/route.ts
  • apps/web/__tests__/ai-choose-rule.test.ts
  • apps/web/__tests__/helpers.ts
  • apps/web/utils/ai/choose-rule/ai-detect-recurring-pattern.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)

**/*.{ts,tsx}: Use createScopedLogger for logging in backend TypeScript files
Typically add the logger initialization at the top of the file when using createScopedLogger
Only use .with() on a logger instance within a specific function, not for a global logger

Import 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.ts
  • apps/web/utils/__mocks__/email-provider.ts
  • apps/web/utils/rule/check-sender-rule-history.ts
  • apps/web/utils/rule/check-sender-rule-history.test.ts
  • apps/web/utils/email/google.ts
  • apps/web/utils/email/microsoft.ts
  • apps/web/app/api/ai/analyze-sender-pattern/route.ts
  • apps/web/__tests__/ai-choose-rule.test.ts
  • apps/web/__tests__/helpers.ts
  • apps/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.ts
  • apps/web/utils/__mocks__/email-provider.ts
  • apps/web/utils/rule/check-sender-rule-history.ts
  • apps/web/utils/rule/check-sender-rule-history.test.ts
  • apps/web/utils/email/google.ts
  • apps/web/utils/email/microsoft.ts
  • apps/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.ts
  • apps/web/utils/__mocks__/email-provider.ts
  • apps/web/utils/rule/check-sender-rule-history.ts
  • apps/web/utils/rule/check-sender-rule-history.test.ts
  • apps/web/utils/email/google.ts
  • apps/web/utils/email/microsoft.ts
  • apps/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 use elements 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.ts
  • apps/web/utils/__mocks__/email-provider.ts
  • apps/web/utils/rule/check-sender-rule-history.ts
  • apps/web/utils/rule/check-sender-rule-history.test.ts
  • apps/web/utils/email/google.ts
  • apps/web/utils/email/microsoft.ts
  • apps/web/app/api/ai/analyze-sender-pattern/route.ts
  • apps/web/__tests__/ai-choose-rule.test.ts
  • apps/web/__tests__/helpers.ts
  • apps/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.ts
  • apps/web/utils/__mocks__/email-provider.ts
  • apps/web/utils/rule/check-sender-rule-history.ts
  • .cursor/rules/testing.mdc
  • apps/web/utils/rule/check-sender-rule-history.test.ts
  • apps/web/utils/email/google.ts
  • apps/web/utils/email/microsoft.ts
  • apps/web/app/api/ai/analyze-sender-pattern/route.ts
  • apps/web/__tests__/ai-choose-rule.test.ts
  • apps/web/__tests__/helpers.ts
  • apps/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.ts
  • apps/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.ts and dir/format.test.ts)
Use vi.mock("server-only", () => ({})); to mock the server-only module in tests
Mock @/utils/prisma in tests using vi.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.ts
  • apps/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.ts
  • apps/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: Use withAuth for user-level operations
Use withEmailAccount for 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 with withAuth or withEmailAccount middleware 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).
Use withEmailAccount middleware for API routes that operate on a specific email account (i.e., use or require emailAccountId).
Use withAuth middleware for API routes that operate at the user level (i.e., use or require only userId).
Use withError middleware (with proper validation) for public endpoints, custom authentication, or cron endpoints.
Cron endpoints MUST use withError middleware and validate the cron secret using hasCronSecret(request) or hasPostCronSecret(request).
Cron endpoints MUST capture unauthorized attempts with captureException and 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.
All findUnique and findFirst Prisma calls in API routes MUST include ownership filters (e.g., userId or emailAccountId).
All findMany Prisma 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.ts
  • apps/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.ts
  • apps/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.ts
  • apps/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.mdc
  • apps/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.mdc
  • apps/web/utils/rule/check-sender-rule-history.test.ts
  • apps/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.ts
  • apps/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 ActionType for fixture data here is pragmatic and low-risk.


5-5: Good move to shared helpers.

Using @/__tests__/helpers keeps tests consistent.


82-82: Adding folderId: null maintains 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.provider field that's now part of the EmailAccountWithAI structure used in the test infrastructure.


44-64: LGTM - Well-structured test helper for rules.

The getRule helper 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_THREADS clearly indicates the unit being measured.


87-95: LGTM - Provider abstraction implementation.

The transition to using createEmailProvider with 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 consistentRuleName to 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 getThreadsWithQuery method properly abstracts the email service implementation while maintaining the same functional behavior.


251-252: LGTM - Provider-consistent message fetching.

Using provider.getThreadMessages maintains 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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between ecc1b88 and 7848f00.

📒 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: Use withAuth for user-level operations
Use withEmailAccount for 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 with withAuth or withEmailAccount middleware 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}: Use createScopedLogger for logging in backend TypeScript files
Typically add the logger initialization at the top of the file when using createScopedLogger
Only use .with() on a logger instance within a specific function, not for a global logger

Import 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).
Use withEmailAccount middleware for API routes that operate on a specific email account (i.e., use or require emailAccountId).
Use withAuth middleware for API routes that operate at the user level (i.e., use or require only userId).
Use withError middleware (with proper validation) for public endpoints, custom authentication, or cron endpoints.
Cron endpoints MUST use withError middleware and validate the cron secret using hasCronSecret(request) or hasPostCronSecret(request).
Cron endpoints MUST capture unauthorized attempts with captureException and 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.
All findUnique and findFirst Prisma calls in API routes MUST include ownership filters (e.g., userId or emailAccountId).
All findMany Prisma 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 use elements 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.

@elie222 elie222 merged commit 526a2a5 into main Sep 20, 2025
7 of 9 checks passed
@elie222 elie222 deleted the feat/improve-learning branch September 20, 2025 18:23
This was referenced Oct 17, 2025
@coderabbitai coderabbitai bot mentioned this pull request Dec 18, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments