Skip to content

Outlook fixes#1103

Merged
elie222 merged 13 commits intomainfrom
fix/outlook-query
Dec 17, 2025
Merged

Outlook fixes#1103
elie222 merged 13 commits intomainfrom
fix/outlook-query

Conversation

@elie222
Copy link
Copy Markdown
Owner

@elie222 elie222 commented Dec 17, 2025

Summary by CodeRabbit

  • New Features

    • Debug Rules page and new "Rules" button to view rules, bulk-toggle all rules, and export rules JSON.
    • Expanded end-to-end test coverage for Gmail and Outlook flows and query parsing.
  • Bug Fixes / Improvements

    • Better wrapping for long assistant result fields.
    • More robust email search/query behavior and more unique test label names to avoid collisions.
    • Improved label error handling and test helper reliability.
  • Removed

    • Rule examples UI and related example endpoints.

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link
Copy Markdown

vercel bot commented Dec 17, 2025

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

Project Deployment Review Updated (UTC)
inbox-zero Ready Ready Preview Dec 17, 2025 4:03am

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Dec 17, 2025

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

Adds Gmail/Microsoft E2E tests and helpers; removes rule/example/group example UI and related APIs; refactors EmailProvider (removes getMessagesByFields) and prior-communication search logic to use a centralized sender search term; adds Debug Rules UI/API and a bulk-toggle action; small mocks/UI/util tweaks; version bump.

Changes

Cohort / File(s) Summary
Cold Email E2E Tests
apps/web/__tests__/e2e/cold-email/google-cold-email.test.ts, apps/web/__tests__/e2e/cold-email/microsoft-cold-email.test.ts, apps/web/__tests__/e2e/outlook-query-parsing.test.ts
New E2E suites for Gmail/Microsoft cold-email detection and Outlook query parsing; env-gated, use real accounts/messages, validate previous-communication detection and query parsing.
E2E Helpers & Ops
apps/web/__tests__/e2e/helpers.ts, apps/web/__tests__/e2e/gmail-operations.test.ts, apps/web/__tests__/e2e/outlook-operations.test.ts
Added ensureTestPremiumAccount and ensureCatchAllTestRule; expanded findOldMessage to prefer inbox and oldest selection; tests updated to call helpers and adjusted timeouts.
Labeling E2E Tests
apps/web/__tests__/e2e/labeling/google-labeling.test.ts, apps/web/__tests__/e2e/labeling/microsoft-labeling.test.ts
Test label names made unique with random suffixes; beforeAll timeouts increased; verbose debug logging reduced.
Examples UI & Types Removed
apps/web/app/(app)/[emailAccountId]/assistant/group/[groupId]/examples/page.tsx, apps/web/app/(app)/[emailAccountId]/assistant/rule/[ruleId]/examples/page.tsx, apps/web/app/(app)/[emailAccountId]/assistant/rule/[ruleId]/examples/example-list.tsx, apps/web/app/(app)/[emailAccountId]/assistant/rule/[ruleId]/examples/types.ts
Deleted rule/examples pages, ExampleList component, and related type definitions/exports.
Group Emails API Removed
apps/web/app/api/user/group/[groupId]/messages/controller.ts, apps/web/app/api/user/group/[groupId]/messages/route.ts, apps/web/app/api/v1/group/[groupId]/emails/route.ts, apps/web/app/api/v1/group/[groupId]/emails/validation.ts, apps/web/app/api/v1/openapi/route.ts
Removed group email retrieval controller, routes, validation schemas, and OpenAPI registration for the group emails endpoint.
Rule Examples API Removed
apps/web/app/api/user/rules/[id]/example/controller.ts, apps/web/app/api/user/rules/[id]/example/route.ts
Removed rule example-fetching controller/route and related exported types.
Debug Rules Feature
apps/web/app/api/user/debug/rules/route.ts, apps/web/app/(app)/[emailAccountId]/debug/rules/page.tsx, apps/web/app/(app)/[emailAccountId]/debug/page.tsx, apps/web/utils/actions/rule.ts, apps/web/utils/actions/rule.validation.ts
Added DebugRulesPage UI, new GET API returning debug rules (DebugRulesResponse), copy-to-clipboard and toggle-all UI, and new toggleAllRulesAction with validation schema.
EmailProvider/API Refactor
apps/web/utils/email/types.ts, apps/web/utils/email/google.ts, apps/web/utils/email/microsoft.ts, apps/web/utils/gmail/message.ts
Removed getMessagesByFields from EmailProvider interface and implementations; refactored Gmail/Outlook provider code to use centralized sender search-term logic and adjusted hasPreviousCommunication helpers (Gmail helpers now accept Gmail client).
Centralized Sender Search Logic
apps/web/utils/email.ts, apps/web/utils/gmail/message.ts, apps/web/utils/email/google.ts, apps/web/utils/email/microsoft.ts
Added PUBLIC_EMAIL_DOMAINS and getSearchTermForSender; migrated prior-communication/search utilities to use domain-vs-email search term behavior.
Gmail/Outlook Label Error Handling
apps/web/utils/gmail/label.ts, apps/web/utils/outlook/label.ts
Use extractErrorInfo(error) to derive error messages in label creation error handlers.
Mocks & Minor UI
apps/web/utils/__mocks__/email-provider.ts, apps/web/components/HoverCard.tsx, apps/web/app/(app)/[emailAccountId]/assistant/ResultDisplay.tsx
Mock: added getThreadsWithParticipant, removed getMessagesByFields; HoverCard className wired via cn; ResultDisplay fields use whitespace-pre-wrap break-all.
Version
version.txt
Bumped version from v2.23.5 to v2.23.6.

Sequence Diagram(s)

sequenceDiagram
    participant UI as DebugRulesPage (client)
    participant API as GET /api/user/debug/rules
    participant Action as toggleAllRulesAction
    participant DB as Prisma

    UI->>API: GET debug rules
    API->>DB: query rules with actions and group counts
    DB-->>API: rules data
    API-->>UI: JSON DebugRulesResponse

    UI->>Action: invoke toggleAllRulesAction(enabled)
    Action->>DB: updateMany rules set enabled
    DB-->>Action: update result
    Action-->>UI: { success: true }
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Areas needing focused review:
    • EmailProvider interface refactor: ensure no remaining uses of removed getMessagesByFields and providers conform to the new interface.
    • Gmail/outlook signature changes: verify call sites pass Gmail client vs provider where signatures changed.
    • Large deletions: search for stray imports/types referencing removed example/group APIs/types (e.g., RuleWithGroup, MessageWithGroupItem, getGroupEmails).
    • Debug rules API & toggle: confirm middleware, response shape, and toggleAllRulesAction mutation scope and validation.
    • E2E tests/helpers: validate env gating and helper side-effects to avoid accidental execution against real accounts.

Possibly related PRs

  • Add debug rules page #1102 — Adds Debug Rules UI/API and toggle actions; closely related to the new Debug Rules feature here.
  • Provider refactor #664 — Refactors EmailProvider and removes getMessagesByFields; directly related to provider/interface changes.
  • Fix outlook cold email #1002 — Modifies Outlook previous-communication logic; overlaps with Outlook search/previous-communication updates.

Suggested reviewers

  • anakarentorosserrano-star
  • mosesjames7271-svg

Poem

🐰 I hopped through code with nimble feet,

I chased old threads and cleaned the suite,
Tests now sniff senders by domain and name,
Debug rules toggle — one click to tame,
Version bumped, carrots earned — hop, hooray! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 31.58% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The PR title 'Outlook fixes' is vague and generic, using a non-descriptive term that doesn't convey meaningful information about the specific changes. Replace with a more specific title that describes the main change, such as 'Fix Outlook query parsing and email provider methods' or 'Refactor Outlook email retrieval and search functionality'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/outlook-query

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.

@macroscopeapp
Copy link
Copy Markdown
Contributor

macroscopeapp bot commented Dec 17, 2025

Update Outlook query handling and premium-rule test setup, add Outlook query parsing E2E, and adjust Gmail/Microsoft labeling tests with 30,000–60,000 ms timeouts

Add Outlook query prefix stripping and domain KQL in outlook.OutlookProvider, introduce Outlook query parsing E2E tests, refactor Gmail previous-communication checks to use the Gmail client, add rules debug API/UI with bulk toggle action, and update E2E suites to ensure premium accounts and catch-all rules with extended timeouts; also remove /group/{groupId}/emails from the OpenAPI spec and bump version.

📍Where to Start

Start with Outlook query changes in getMessagesWithPagination and previous-communication logic in apps/web/utils/email/microsoft.ts, then review the new E2E coverage in apps/web/tests/e2e/outlook-query-parsing.test.ts.


Macroscope summarized 3cb430e.

.top(options?.maxResults || 50)
.get();

return response.value.map((msg) => convertMessage(msg));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggestion: Guard response.value in getDrafts to avoid a runtime error if it’s undefined. Consider using the same (response.value || []) pattern used elsewhere in this class.

Suggested change
return response.value.map((msg) => convertMessage(msg));
return (response.value || []).map((msg) => convertMessage(msg));

🚀 Reply to ask Macroscope to explain or update this suggestion.

👍 Helpful? React to give us feedback.

Comment on lines 971 to 980
async getDrafts(options?: { maxResults?: number }): Promise<ParsedMessage[]> {
const response = await this.getMessagesWithPagination({
query: "isDraft eq true",
maxResults: options?.maxResults || 50,
});
return response.messages;
const response: { value: Message[] } = await this.client
.getClient()
.api("/me/mailFolders/drafts/messages")
.select(MESSAGE_SELECT_FIELDS)
.top(options?.maxResults || 50)
.get();

return response.value.map((msg) => convertMessage(msg));
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

convertMessage is called without folderIds, so labelIds won’t map correctly. Consider fetching folderIds via getFolderIds(this.client) and passing it to convertMessage.

Suggested change
async getDrafts(options?: { maxResults?: number }): Promise<ParsedMessage[]> {
const response = await this.getMessagesWithPagination({
query: "isDraft eq true",
maxResults: options?.maxResults || 50,
});
return response.messages;
const response: { value: Message[] } = await this.client
.getClient()
.api("/me/mailFolders/drafts/messages")
.select(MESSAGE_SELECT_FIELDS)
.top(options?.maxResults || 50)
.get();
return response.value.map((msg) => convertMessage(msg));
}
async getDrafts(options?: { maxResults?: number }): Promise<ParsedMessage[]> {
const response: { value: Message[] } = await this.client
.getClient()
.api("/me/mailFolders/drafts/messages")
.select(MESSAGE_SELECT_FIELDS)
.top(options?.maxResults || 50)
.get();
const folderIds = await getFolderIds(this.client);
return response.value.map((msg) => convertMessage(msg, folderIds));
}

🚀 Reply to ask Macroscope to explain or update this suggestion.

👍 Helpful? React to give us feedback.

Copy link
Copy Markdown
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: 2

🧹 Nitpick comments (3)
apps/web/app/(app)/[emailAccountId]/assistant/ResultDisplay.tsx (1)

216-219: Consider break-words instead of break-all for better readability.

While break-all prevents overflow, it breaks words at any character, which can reduce readability for fields like subject and content. The break-words class breaks long words only when necessary while preserving word boundaries when possible, providing a better user experience for mixed content types (emails, URLs, and regular text).

Apply this diff to use break-words:

                  <div
                    key={field.key}
-                    className="whitespace-pre-wrap break-all"
+                    className="whitespace-pre-wrap break-words"
                  >
apps/web/utils/email/google.ts (1)

946-958: Consider potential N+1 query pattern.

The current implementation lists drafts and then fetches each draft's full message individually. For the default maxResults of 50, this results in 51 API calls. While parallel fetching with Promise.all helps, consider whether batch operations could be more efficient if available in the Gmail API.

apps/web/__tests__/e2e/cold-email/microsoft-cold-email.test.ts (1)

135-151: Consider using test.skipIf for clearer skip semantics.

The early return works, but test.skipIf(!companyDomain) would make the skip condition more explicit in test output and follow vitest conventions.

-    test("returns TRUE for colleague at same company domain", async () => {
-      if (!companyDomain) {
-        // No company domain in inbox - skip
-        return;
-      }
+    test.skipIf(!companyDomain)("returns TRUE for colleague at same company domain", async () => {

Note: This would require lifting companyDomain to module scope or using a different pattern since test.skipIf evaluates at test registration time. The current approach is acceptable if companyDomain must be determined at runtime.

Comment on lines 971 to 980
async getDrafts(options?: { maxResults?: number }): Promise<ParsedMessage[]> {
const response = await this.getMessagesWithPagination({
query: "isDraft eq true",
maxResults: options?.maxResults || 50,
});
return response.messages;
const response: { value: Message[] } = await this.client
.getClient()
.api("/me/mailFolders/drafts/messages")
.select(MESSAGE_SELECT_FIELDS)
.top(options?.maxResults || 50)
.get();

return response.value.map((msg) => convertMessage(msg));
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Search for convertMessage function definition
rg -n "function convertMessage|const convertMessage|export.*convertMessage" apps/web --type ts --type tsx -A 3

# Also check for convertMessage type definition
rg -n "convertMessage\(" apps/web/utils/email/microsoft.ts -B 2 -A 2

# Find where folderIds is used in relation to convertMessage
rg -n "folderIds" apps/web/utils/email/microsoft.ts -B 3 -A 3

Repository: elie222/inbox-zero

Length of output: 1815


🏁 Script executed:

# Search for convertMessage function definition more broadly
rg -n "convertMessage" apps/web/utils --type ts -B 2 -A 15 | head -100

# Also check the imports in microsoft.ts
rg -n "import.*convertMessage|export.*convertMessage" apps/web -type ts -A 2 -B 2

Repository: elie222/inbox-zero

Length of output: 6806


🏁 Script executed:

# Find the convertMessage function definition in message.ts
cat apps/web/utils/outlook/message.ts | head -150

Repository: elie222/inbox-zero

Length of output: 4484


🏁 Script executed:

# Continue reading to find convertMessage function
sed -n '140,280p' apps/web/utils/outlook/message.ts

Repository: elie222/inbox-zero

Length of output: 4624


🏁 Script executed:

# Find the convertMessage function definition
rg -n "^export function convertMessage|^function convertMessage" apps/web/utils/outlook/message.ts -A 30

Repository: elie222/inbox-zero

Length of output: 1204


🏁 Script executed:

# Check if there are other instances where convertMessage is called without folderIds
rg -n "convertMessage\(" apps/web --type ts -B 2 -A 1 | grep -E "convertMessage\(|apps/web"

Repository: elie222/inbox-zero

Length of output: 2467


Pass folderIds to convertMessage for consistency with other message-fetching methods.

The convertMessage function has an optional folderIds parameter (defaults to {}). While this won't cause errors, omitting it disables folder-based label mapping. Other similar methods (getSentMessages, getInboxMessages) explicitly pass folderIds to ensure proper label mapping. Update line 979 to fetch and pass folderIds:

const folderIds = await getFolderIds(this.client);
return response.value.map((msg) => convertMessage(msg, folderIds));

This maintains consistency and ensures drafts receive proper folder-related labels.

🤖 Prompt for AI Agents
In apps/web/utils/email/microsoft.ts around lines 971 to 980, the draft messages
are converted without passing folderIds which disables folder-based label
mapping; fetch folder IDs with getFolderIds(this.client) (await it) and pass the
resulting folderIds into convertMessage when mapping response.value so each
draft is converted as convertMessage(msg, folderIds).

Copy link
Copy Markdown
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.

5 issues found across 35 files

Prompt for AI agents (all 5 issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="apps/web/app/(app)/[emailAccountId]/debug/rules/page.tsx">

<violation number="1" location="apps/web/app/(app)/[emailAccountId]/debug/rules/page.tsx:44">
P2: Clipboard write operation is not awaited and lacks error handling. If the clipboard operation fails, the UI will still show &quot;Copied&quot; and display the success toast.</violation>
</file>

<file name="apps/web/utils/email.ts">

<violation number="1" location="apps/web/utils/email.ts:141">
P2: When input is in &quot;Name &lt;email&gt;&quot; format and domain is public, this returns the entire string including the name instead of just the email address. Use `extractEmailAddress(email)` to ensure consistent return format as documented (&quot;returns the full email address&quot;).</violation>
</file>

<file name="apps/web/__tests__/e2e/helpers.ts">

<violation number="1" location="apps/web/__tests__/e2e/helpers.ts:83">
P2: Including `enabled: true` in the query will cause duplicate rules to be created if the test rule exists but is disabled. Consider finding by name only, then enabling an existing disabled rule or creating new if not found.</violation>
</file>

<file name="apps/web/__tests__/e2e/outlook-query-parsing.test.ts">

<violation number="1" location="apps/web/__tests__/e2e/outlook-query-parsing.test.ts:26">
P2: Tests will fail with cryptic errors if `TEST_OUTLOOK_EMAIL` is not set because `provider` will be undefined. Include it in the skipIf condition to match the pattern used by other E2E tests in this codebase.</violation>
</file>

<file name="apps/web/__tests__/e2e/cold-email/microsoft-cold-email.test.ts">

<violation number="1" location="apps/web/__tests__/e2e/cold-email/microsoft-cold-email.test.ts:136">
P2: Test silently passes instead of properly skipping when `companyDomain` is undefined. The comment says &quot;skip&quot; but `return` makes the test pass, not skip. Consider using vitest&#39;s dynamic skip mechanism or updating the comment to reflect the actual behavior.</violation>
</file>

Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR

const existingRule = await prisma.rule.findFirst({
where: {
emailAccountId,
enabled: true,
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Dec 17, 2025

Choose a reason for hiding this comment

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

P2: Including enabled: true in the query will cause duplicate rules to be created if the test rule exists but is disabled. Consider finding by name only, then enabling an existing disabled rule or creating new if not found.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/__tests__/e2e/helpers.ts, line 83:

<comment>Including `enabled: true` in the query will cause duplicate rules to be created if the test rule exists but is disabled. Consider finding by name only, then enabling an existing disabled rule or creating new if not found.</comment>

<file context>
@@ -26,3 +27,80 @@ export async function findOldMessage(
+  const existingRule = await prisma.rule.findFirst({
+    where: {
+      emailAccountId,
+      enabled: true,
+      name: &quot;E2E Test Catch-All Rule&quot;,
+    },
</file context>
Fix with Cubic


vi.mock("server-only", () => ({}));

describe.skipIf(!RUN_E2E_TESTS)(
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Dec 17, 2025

Choose a reason for hiding this comment

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

P2: Tests will fail with cryptic errors if TEST_OUTLOOK_EMAIL is not set because provider will be undefined. Include it in the skipIf condition to match the pattern used by other E2E tests in this codebase.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/__tests__/e2e/outlook-query-parsing.test.ts, line 26:

<comment>Tests will fail with cryptic errors if `TEST_OUTLOOK_EMAIL` is not set because `provider` will be undefined. Include it in the skipIf condition to match the pattern used by other E2E tests in this codebase.</comment>

<file context>
@@ -0,0 +1,174 @@
+
+vi.mock(&quot;server-only&quot;, () =&gt; ({}));
+
+describe.skipIf(!RUN_E2E_TESTS)(
+  &quot;Outlook Query Parsing E2E&quot;,
+  { timeout: 15_000 },
</file context>
Fix with Cubic

Copy link
Copy Markdown
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

♻️ Duplicate comments (1)
apps/web/__tests__/e2e/helpers.ts (1)

94-100: Duplicate rule creation when disabled rule exists.

Including enabled: true in the findFirst query means if a rule with name "E2E Test Catch-All Rule" exists but is disabled, the function will create a duplicate rule instead of enabling the existing one.

Consider finding by name and emailAccountId only, then enable if disabled or create if not found:

  const existingRule = await prisma.rule.findFirst({
    where: {
      emailAccountId,
-     enabled: true,
      name: "E2E Test Catch-All Rule",
    },
  });

  if (!existingRule) {
    await prisma.rule.create({
      data: {
        name: "E2E Test Catch-All Rule",
        emailAccountId,
        enabled: true,
        automate: true,
        instructions:
          "This is a test rule that should match all emails. Draft a brief acknowledgment reply.",
        actions: {
          create: {
            type: "DRAFT_EMAIL",
            content: "Test acknowledgment",
          },
        },
      },
    });
+  } else if (!existingRule.enabled) {
+    await prisma.rule.update({
+      where: { id: existingRule.id },
+      data: { enabled: true },
+    });
  }
🧹 Nitpick comments (2)
apps/web/utils/email.ts (1)

116-132: Consider expanding the list of public domains.

The current list covers major providers. If you encounter other common public email providers in production (e.g., live.com, msn.com, mail.com, inbox.com), consider adding them to ensure consistent search behavior.

apps/web/__tests__/e2e/cold-email/google-cold-email.test.ts (1)

84-94: Consider storing the extracted email to avoid redundant calls.

extractEmailAddress is called twice on the same value: once in the find predicate (line 85) and again at line 92. You could store the result from the find logic to avoid the redundant call.

Example optimization:

-      const companyMessage = realMessages.find((m) => {
-        const from = extractEmailAddress(m.headers.from);
-        if (!from) return false;
-        const domain = extractDomainFromEmail(from);
-        return domain && !publicDomains.includes(domain.toLowerCase());
-      });
-
-      if (companyMessage) {
-        const senderEmail = extractEmailAddress(companyMessage.headers.from)!;
-        companyDomain = extractDomainFromEmail(senderEmail) || undefined;
-      }
+      let companySenderEmail: string | undefined;
+      const companyMessage = realMessages.find((m) => {
+        const from = extractEmailAddress(m.headers.from);
+        if (!from) return false;
+        const domain = extractDomainFromEmail(from);
+        if (domain && !publicDomains.includes(domain.toLowerCase())) {
+          companySenderEmail = from;
+          return true;
+        }
+        return false;
+      });
+
+      if (companyMessage && companySenderEmail) {
+        companyDomain = extractDomainFromEmail(companySenderEmail) || undefined;
+      }
📜 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 ccafe1a and e7e4be9.

📒 Files selected for processing (5)
  • apps/web/__tests__/e2e/cold-email/google-cold-email.test.ts (1 hunks)
  • apps/web/__tests__/e2e/cold-email/microsoft-cold-email.test.ts (1 hunks)
  • apps/web/__tests__/e2e/helpers.ts (2 hunks)
  • apps/web/utils/email.ts (1 hunks)
  • apps/web/utils/logger.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/tests/e2e/cold-email/microsoft-cold-email.test.ts
🧰 Additional context used
📓 Path-based instructions (15)
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Use @/ path aliases for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Follow consistent naming conventions using PascalCase for components
Centralize shared types in dedicated type files

Import specific lodash functions rather than entire lodash library to minimize bundle size (e.g., import groupBy from 'lodash/groupBy')

Files:

  • apps/web/__tests__/e2e/helpers.ts
  • apps/web/utils/email.ts
  • apps/web/utils/logger.ts
  • apps/web/__tests__/e2e/cold-email/google-cold-email.test.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/data-fetching.mdc)

**/*.{ts,tsx}: For API GET requests to server, use the swr package
Use result?.serverError with toastError from @/components/Toast for error handling in async operations

**/*.{ts,tsx}: Use wrapper functions for Gmail message operations (get, list, batch, etc.) from @/utils/gmail/message.ts instead of direct API calls
Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls
Use wrapper functions for Gmail label operations from @/utils/gmail/label.ts instead of direct API calls

**/*.{ts,tsx}: For early access feature flags, create hooks using the naming convention use[FeatureName]Enabled that return a boolean from useFeatureFlagEnabled("flag-key")
For A/B test variant flags, create hooks using the naming convention use[FeatureName]Variant that define variant types, use useFeatureFlagVariantKey() with type casting, and provide a default "control" fallback
Use kebab-case for PostHog feature flag keys (e.g., inbox-cleaner, pricing-options-2)
Always define types for A/B test variant flags (e.g., type PricingVariant = "control" | "variant-a" | "variant-b") and provide type safety through type casting

**/*.{ts,tsx}: Don't use primitive type aliases or misleading types
Don't use empty type parameters in type aliases and interfaces
Don't use this and super in static contexts
Don't use any or unknown as type constraints
Don't use the TypeScript directive @ts-ignore
Don't use TypeScript enums
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 TypeScript namespaces
Don't use non-null assertions with the ! postfix operator
Don't use parameter properties in class constructors
Don't use user-defined types
Use as const instead of literal types and type annotations
Use either T[] or Array<T> consistently
Initialize each enum member value explicitly
Use export type for types
Use `impo...

Files:

  • apps/web/__tests__/e2e/helpers.ts
  • apps/web/utils/email.ts
  • apps/web/utils/logger.ts
  • apps/web/__tests__/e2e/cold-email/google-cold-email.test.ts
apps/web/{utils/ai,utils/llms,__tests__}/**/*.ts

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

LLM-related code must be organized in specific directories: apps/web/utils/ai/ for main implementations, apps/web/utils/llms/ for core utilities and configurations, and apps/web/__tests__/ for LLM-specific tests

Files:

  • apps/web/__tests__/e2e/helpers.ts
  • apps/web/__tests__/e2e/cold-email/google-cold-email.test.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/prisma-enum-imports.mdc)

Always import Prisma enums from @/generated/prisma/enums instead of @/generated/prisma/client to avoid Next.js bundling errors in client components

Import Prisma using the project's centralized utility: import prisma from '@/utils/prisma'

Files:

  • apps/web/__tests__/e2e/helpers.ts
  • apps/web/utils/email.ts
  • apps/web/utils/logger.ts
  • apps/web/__tests__/e2e/cold-email/google-cold-email.test.ts
**/*.ts

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

**/*.ts: ALL database queries MUST be scoped to the authenticated user/account by including user/account filtering in WHERE clauses to prevent unauthorized data access
Always validate that resources belong to the authenticated user before performing operations, using ownership checks in WHERE clauses or relationships
Always validate all input parameters for type, format, and length before using them in database queries
Use SafeError for error responses to prevent information disclosure. Generic error messages should not reveal internal IDs, logic, or resource ownership details
Only return necessary fields in API responses using Prisma's select option. Never expose sensitive data such as password hashes, private keys, or system flags
Prevent Insecure Direct Object References (IDOR) by validating resource ownership before operations. All findUnique/findFirst calls MUST include ownership filters
Prevent mass assignment vulnerabilities by explicitly whitelisting allowed fields in update operations instead of accepting all user-provided data
Prevent privilege escalation by never allowing users to modify system fields, ownership fields, or admin-only attributes through user input
All findMany queries MUST be scoped to the user's data by including appropriate WHERE filters to prevent returning data from other users
Use Prisma relationships for access control by leveraging nested where clauses (e.g., emailAccount: { id: emailAccountId }) to validate ownership

Files:

  • apps/web/__tests__/e2e/helpers.ts
  • apps/web/utils/email.ts
  • apps/web/utils/logger.ts
  • apps/web/__tests__/e2e/cold-email/google-cold-email.test.ts
**/__tests__/**/*.{ts,tsx}

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

AI tests must be placed in the __tests__ directory and are not run by default (they use a real LLM)

Files:

  • apps/web/__tests__/e2e/helpers.ts
  • apps/web/__tests__/e2e/cold-email/google-cold-email.test.ts
**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)

**/*.{tsx,ts}: Use Shadcn UI and Tailwind for components and styling
Use next/image package for images
For API GET requests to server, use the swr package with hooks like useSWR to fetch data
For text inputs, use the Input component with registerProps for form integration and error handling

Files:

  • apps/web/__tests__/e2e/helpers.ts
  • apps/web/utils/email.ts
  • apps/web/utils/logger.ts
  • apps/web/__tests__/e2e/cold-email/google-cold-email.test.ts
**/*.{tsx,ts,css}

📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)

Implement responsive design with Tailwind CSS using a mobile-first approach

Files:

  • apps/web/__tests__/e2e/helpers.ts
  • apps/web/utils/email.ts
  • apps/web/utils/logger.ts
  • apps/web/__tests__/e2e/cold-email/google-cold-email.test.ts
**/*.{js,jsx,ts,tsx}

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

**/*.{js,jsx,ts,tsx}: Don't use accessKey attribute on any HTML element
Don't set aria-hidden="true" on focusable elements
Don't add ARIA roles, states, and properties to elements that don't support them
Don't use distracting elements like <marquee> or <blink>
Only use the scope prop on <th> elements
Don't assign non-interactive ARIA roles to interactive HTML elements
Make sure label elements have text content and are associated with an input
Don't assign interactive ARIA roles to non-interactive HTML elements
Don't assign tabIndex to non-interactive HTML elements
Don't use positive integers for tabIndex property
Don't include "image", "picture", or "photo" in img alt prop
Don't use explicit role property that's the same as the implicit/default role
Make static elements with click handlers use a valid role attribute
Always include a title element for SVG elements
Give all elements requiring alt text meaningful information for screen readers
Make sure anchors have content that's accessible to screen readers
Assign tabIndex to non-interactive HTML elements with aria-activedescendant
Include all required ARIA attributes for elements with ARIA roles
Make sure ARIA properties are valid for the element's supported roles
Always include a type attribute for button elements
Make elements with interactive roles and handlers focusable
Give heading elements content that's accessible to screen readers (not hidden with aria-hidden)
Always include a lang attribute on the html element
Always include a title attribute for iframe elements
Accompany onClick with at least one of: onKeyUp, onKeyDown, or onKeyPress
Accompany onMouseOver/onMouseOut with onFocus/onBlur
Include caption tracks for audio and video elements
Use semantic elements instead of role attributes in JSX
Make sure all anchors are valid and navigable
Ensure all ARIA properties (aria-*) are valid
Use valid, non-abstract ARIA roles for elements with ARIA roles
Use valid AR...

Files:

  • apps/web/__tests__/e2e/helpers.ts
  • apps/web/utils/email.ts
  • apps/web/utils/logger.ts
  • apps/web/__tests__/e2e/cold-email/google-cold-email.test.ts
!(pages/_document).{jsx,tsx}

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

Don't use the next/head module in pages/_document.js on Next.js projects

Files:

  • apps/web/__tests__/e2e/helpers.ts
  • apps/web/utils/email.ts
  • apps/web/utils/logger.ts
  • apps/web/__tests__/e2e/cold-email/google-cold-email.test.ts
**/*.{js,ts,jsx,tsx}

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

**/*.{js,ts,jsx,tsx}: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size (e.g., import groupBy from 'lodash/groupBy')

Files:

  • apps/web/__tests__/e2e/helpers.ts
  • apps/web/utils/email.ts
  • apps/web/utils/logger.ts
  • apps/web/__tests__/e2e/cold-email/google-cold-email.test.ts
**/{server,api,actions,utils}/**/*.ts

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

**/{server,api,actions,utils}/**/*.ts: Use createScopedLogger from "@/utils/logger" for logging in backend code
Add the createScopedLogger instantiation at the top of the file with an appropriate scope name
Use .with() method to attach context variables only within specific functions, not on global loggers
For large functions with reused variables, use createScopedLogger().with() to attach context once and reuse the logger without passing variables repeatedly

Files:

  • apps/web/utils/email.ts
  • apps/web/utils/logger.ts
apps/web/__tests__/**/*.test.ts

📄 CodeRabbit inference engine (.cursor/rules/llm-test.mdc)

apps/web/__tests__/**/*.test.ts: Place all LLM-related tests in apps/web/__tests__/ directory
Use vitest imports (describe, expect, test, vi, beforeEach) in LLM test files
Mock 'server-only' module with empty object in LLM test files: vi.mock("server-only", () => ({}))
Set timeout constant const TIMEOUT = 15_000; for LLM tests
Use describe.runIf(isAiTest) with environment variable RUN_AI_TESTS === "true" to conditionally run LLM tests
Use console.debug() for outputting generated LLM content in tests, e.g., console.debug("Generated content:\n", result.content);
Prefer using existing helpers from @/__tests__/helpers.ts (getEmailAccount, getEmail, getRule, getMockMessage, getMockExecutedRule) instead of creating custom test data helpers

Files:

  • apps/web/__tests__/e2e/cold-email/google-cold-email.test.ts
**/*.test.{ts,tsx}

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

**/*.test.{ts,tsx}: Use vitest for testing the application
Tests should be colocated next to the tested file with .test.ts or .test.tsx extension (e.g., dir/format.ts and dir/format.test.ts)
Mock server-only using vi.mock("server-only", () => ({}))
Mock Prisma using vi.mock("@/utils/prisma") and import the mock from @/utils/__mocks__/prisma
Use vi.clearAllMocks() in beforeEach to clean up mocks between tests
Each test should be independent
Use descriptive test names
Mock external dependencies in tests
Do not mock the Logger
Avoid testing implementation details
Use test helpers getEmail, getEmailAccount, and getRule from @/__tests__/helpers for mocking emails, accounts, and rules

Files:

  • apps/web/__tests__/e2e/cold-email/google-cold-email.test.ts
**/*.{test,spec}.{js,jsx,ts,tsx}

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

**/*.{test,spec}.{js,jsx,ts,tsx}: Don't nest describe() blocks too deeply in test files
Don't use callbacks in asynchronous tests and hooks
Don't have duplicate hooks in describe blocks
Don't use export or module.exports in test files
Don't use focused tests
Make sure the assertion function, like expect, is placed inside an it() function call
Don't use disabled tests

Files:

  • apps/web/__tests__/e2e/cold-email/google-cold-email.test.ts
🧠 Learnings (26)
📓 Common learnings
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-25T14:40:00.833Z
Learning: Applies to **/*.test.{ts,tsx} : Use test helpers `getEmail`, `getEmailAccount`, and `getRule` from `@/__tests__/helpers` for mocking emails, accounts, and rules
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Design Gmail wrapper functions to be provider-agnostic to support future email providers like Outlook and ProtonMail
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Prefer using existing helpers from `@/__tests__/helpers.ts` (`getEmailAccount`, `getEmail`, `getRule`, `getMockMessage`, `getMockExecutedRule`) instead of creating custom test data helpers
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail message operations (get, list, batch, etc.) from @/utils/gmail/message.ts instead of direct API calls
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to apps/web/utils/gmail/**/*.{ts,tsx} : Always use wrapper functions from @/utils/gmail/ for Gmail API operations instead of direct provider API calls
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to apps/web/utils/gmail/**/*.{ts,tsx} : Keep Gmail provider-specific implementation details isolated within the apps/web/utils/gmail/ directory
📚 Learning: 2025-11-25T14:37:56.430Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Prefer using existing helpers from `@/__tests__/helpers.ts` (`getEmailAccount`, `getEmail`, `getRule`, `getMockMessage`, `getMockExecutedRule`) instead of creating custom test data helpers

Applied to files:

  • apps/web/__tests__/e2e/helpers.ts
  • apps/web/__tests__/e2e/cold-email/google-cold-email.test.ts
📚 Learning: 2025-11-25T14:40:00.833Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-25T14:40:00.833Z
Learning: Applies to **/*.test.{ts,tsx} : Use test helpers `getEmail`, `getEmailAccount`, and `getRule` from `@/__tests__/helpers` for mocking emails, accounts, and rules

Applied to files:

  • apps/web/__tests__/e2e/helpers.ts
  • apps/web/utils/email.ts
  • apps/web/__tests__/e2e/cold-email/google-cold-email.test.ts
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls

Applied to files:

  • apps/web/__tests__/e2e/helpers.ts
  • apps/web/utils/email.ts
  • apps/web/__tests__/e2e/cold-email/google-cold-email.test.ts
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail message operations (get, list, batch, etc.) from @/utils/gmail/message.ts instead of direct API calls

Applied to files:

  • apps/web/__tests__/e2e/helpers.ts
  • apps/web/utils/email.ts
  • apps/web/__tests__/e2e/cold-email/google-cold-email.test.ts
📚 Learning: 2025-11-25T14:39:27.909Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:27.909Z
Learning: Applies to **/app/api/**/*.ts : Use `withEmailAccount` middleware for operations scoped to a specific email account, including reading/writing emails, rules, schedules, or any operation using `emailAccountId`

Applied to files:

  • apps/web/__tests__/e2e/helpers.ts
📚 Learning: 2025-11-25T14:39:23.326Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:23.326Z
Learning: Applies to app/api/**/*.ts : Use `withEmailAccount` middleware for operations scoped to a specific email account (reading/writing emails, rules, schedules, etc.) - provides `emailAccountId`, `userId`, and `email` in `request.auth`

Applied to files:

  • apps/web/__tests__/e2e/helpers.ts
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to apps/web/utils/gmail/**/*.{ts,tsx} : Always use wrapper functions from @/utils/gmail/ for Gmail API operations instead of direct provider API calls

Applied to files:

  • apps/web/__tests__/e2e/helpers.ts
  • apps/web/utils/email.ts
  • apps/web/__tests__/e2e/cold-email/google-cold-email.test.ts
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to apps/web/utils/gmail/**/*.{ts,tsx} : Keep Gmail provider-specific implementation details isolated within the apps/web/utils/gmail/ directory

Applied to files:

  • apps/web/__tests__/e2e/helpers.ts
  • apps/web/utils/email.ts
  • apps/web/__tests__/e2e/cold-email/google-cold-email.test.ts
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail label operations from @/utils/gmail/label.ts instead of direct API calls

Applied to files:

  • apps/web/__tests__/e2e/helpers.ts
  • apps/web/utils/email.ts
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : LLM feature functions must import from `zod` for schema validation, use `createScopedLogger` from `@/utils/logger`, `chatCompletionObject` and `createGenerateObject` from `@/utils/llms`, and import `EmailAccountWithAI` type from `@/utils/llms/types`

Applied to files:

  • apps/web/__tests__/e2e/helpers.ts
📚 Learning: 2025-11-25T14:39:08.150Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-11-25T14:39:08.150Z
Learning: Applies to apps/web/app/api/**/*.{ts,tsx} : All database queries must include user scoping with `emailAccountId` or `userId` filtering in WHERE clauses

Applied to files:

  • apps/web/__tests__/e2e/helpers.ts
📚 Learning: 2025-11-25T14:40:00.833Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-25T14:40:00.833Z
Learning: Applies to **/*.test.{ts,tsx} : Mock Prisma using `vi.mock("@/utils/prisma")` and import the mock from `@/utils/__mocks__/prisma`

Applied to files:

  • apps/web/__tests__/e2e/helpers.ts
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : LLM feature functions must follow a standard structure: accept options with `inputData` and `emailAccount` parameters, implement input validation with early returns, define separate system and user prompts, create a Zod schema for response validation, and use `createGenerateObject` to execute the LLM call

Applied to files:

  • apps/web/__tests__/e2e/helpers.ts
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Design Gmail wrapper functions to be provider-agnostic to support future email providers like Outlook and ProtonMail

Applied to files:

  • apps/web/utils/email.ts
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : Use descriptive scoped loggers for each LLM feature, log inputs and outputs with appropriate log levels, and include relevant context in log messages

Applied to files:

  • apps/web/utils/logger.ts
📚 Learning: 2025-11-25T14:38:08.183Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/logging.mdc:0-0
Timestamp: 2025-11-25T14:38:08.183Z
Learning: Applies to **/{server,api,actions,utils}/**/*.ts : Add the `createScopedLogger` instantiation at the top of the file with an appropriate scope name

Applied to files:

  • apps/web/utils/logger.ts
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't hardcode sensitive data like API keys and tokens

Applied to files:

  • apps/web/utils/logger.ts
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : Use XML-like tags to structure data in prompts, remove excessive whitespace and truncate long inputs, and format data consistently across similar LLM functions

Applied to files:

  • apps/web/utils/logger.ts
📚 Learning: 2025-11-25T14:38:08.183Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/logging.mdc:0-0
Timestamp: 2025-11-25T14:38:08.183Z
Learning: Applies to **/{server,api,actions,utils}/**/*.ts : For large functions with reused variables, use `createScopedLogger().with()` to attach context once and reuse the logger without passing variables repeatedly

Applied to files:

  • apps/web/utils/logger.ts
📚 Learning: 2025-11-25T14:39:27.909Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:27.909Z
Learning: Applies to **/*.ts : Only return necessary fields in API responses using Prisma's `select` option. Never expose sensitive data such as password hashes, private keys, or system flags

Applied to files:

  • apps/web/utils/logger.ts
📚 Learning: 2025-11-25T14:38:08.183Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/logging.mdc:0-0
Timestamp: 2025-11-25T14:38:08.183Z
Learning: Applies to **/{server,api,actions,utils}/**/*.ts : Use `createScopedLogger` from "@/utils/logger" for logging in backend code

Applied to files:

  • apps/web/utils/logger.ts
📚 Learning: 2025-11-25T14:37:56.430Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Use vitest imports (`describe`, `expect`, `test`, `vi`, `beforeEach`) in LLM test files

Applied to files:

  • apps/web/__tests__/e2e/cold-email/google-cold-email.test.ts
📚 Learning: 2025-11-25T14:37:56.430Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Use `describe.runIf(isAiTest)` with environment variable `RUN_AI_TESTS === "true"` to conditionally run LLM tests

Applied to files:

  • apps/web/__tests__/e2e/cold-email/google-cold-email.test.ts
📚 Learning: 2025-11-25T14:40:00.833Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-25T14:40:00.833Z
Learning: Applies to **/__tests__/**/*.{ts,tsx} : AI tests must be placed in the `__tests__` directory and are not run by default (they use a real LLM)

Applied to files:

  • apps/web/__tests__/e2e/cold-email/google-cold-email.test.ts
📚 Learning: 2025-11-25T14:40:00.833Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-25T14:40:00.833Z
Learning: Applies to **/*.test.{ts,tsx} : Avoid testing implementation details

Applied to files:

  • apps/web/__tests__/e2e/cold-email/google-cold-email.test.ts
🧬 Code graph analysis (2)
apps/web/utils/logger.ts (1)
apps/web/env.ts (1)
  • env (17-255)
apps/web/__tests__/e2e/cold-email/google-cold-email.test.ts (2)
apps/web/utils/email/provider.ts (1)
  • createEmailProvider (14-32)
apps/web/utils/email.ts (2)
  • extractEmailAddress (19-52)
  • extractDomainFromEmail (73-91)
⏰ 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: test
  • GitHub Check: Review for correctness
🔇 Additional comments (8)
apps/web/utils/logger.ts (1)

255-259: LGTM! Formatting improvement enhances readability.

The multi-line formatting of this conditional expression improves code readability without any behavioral changes. The split is consistent with the similar formatting pattern used in the subsequent else-if block at lines 263-268.

apps/web/utils/email.ts (1)

137-144: LGTM! Previous issues addressed.

The implementation correctly extracts the email address for public domains (with proper fallback) and returns the domain for company emails. The logic handles edge cases appropriately and addresses the concerns from previous review comments.

apps/web/__tests__/e2e/cold-email/google-cold-email.test.ts (3)

1-30: LGTM! Well-structured E2E test setup.

The test file follows E2E patterns correctly: proper vitest imports, server-only mocking, environment-based gating with describe.skipIf, and appropriate timeout configuration for real API calls.


37-75: LGTM! Test setup logic is sound.

The beforeAll hook properly initializes the provider with real account data and derives test inputs from actual inbox messages. Error handling ensures tests fail fast if prerequisites aren't met.


97-154: LGTM! Comprehensive test coverage.

The four test cases provide thorough coverage of the hasPreviousCommunicationsWithSenderOrDomain method: known sender (TRUE), unknown sender (FALSE), pre-email date (FALSE), and company domain colleague (TRUE with conditional skip). The use of skip() for the company domain test when no suitable data exists is appropriate.

apps/web/__tests__/e2e/helpers.ts (3)

5-5: LGTM! Correct prisma import.

Using the centralized prisma wrapper as per project guidelines.


8-43: LGTM! Robust message selection logic.

The enhanced findOldMessage correctly prioritizes older messages to avoid interfering with recent activity, with a sensible fallback to the oldest available inbox message. Error handling ensures clear feedback when no suitable message exists.


45-83: LGTM! Proper premium setup for E2E tests.

ensureTestPremiumAccount correctly ensures premium status for AI feature testing. Database operations are properly scoped with userId, and the logic handles both create and update scenarios cleanly.

Co-authored-by: macroscopeapp[bot] <170038800+macroscopeapp[bot]@users.noreply.github.com>
Comment on lines +1301 to +1313
.top(5)
.select("id,sentDateTime")
.get()
.catch((error) => {
this.logger.error("Error checking sent messages (domain)", {
error,
});
return { value: [] };
}),

this.client
.getClient()
.api("/me/messages")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Domain search uses to:@domain/from:@domain, which Graph KQL doesn’t reliably match. This likely yields false negatives. Consider switching to participants:@domain for the domain-based search (and keep the date filtering).

-             .search(`"to:@${escapedKqlDomain}"`)
+             .search(`participants:@${escapedKqlDomain}`)
-             .search(`"from:@${escapedKqlDomain}"`)
+             .search(`participants:@${escapedKqlDomain}`)

🚀 Reply to ask Macroscope to explain or update this suggestion.

👍 Helpful? React to give us feedback.

Copy link
Copy Markdown
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 (1)
apps/web/__tests__/e2e/cold-email/microsoft-cold-email.test.ts (1)

27-36: Use the centralized PUBLIC_EMAIL_DOMAINS constant from @/utils/email.

This test duplicates the public email domains list. Import and use PUBLIC_EMAIL_DOMAINS from @/utils/email instead. Additionally, the domains live.com and msn.com (which this test includes) are not in the shared constant—consider adding them there if they represent a broader set of public providers.

📜 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 b264f40 and 3cb430e.

📒 Files selected for processing (1)
  • apps/web/__tests__/e2e/cold-email/microsoft-cold-email.test.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (14)
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/web/CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Use @/ path aliases for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Follow consistent naming conventions using PascalCase for components
Centralize shared types in dedicated type files

Import specific lodash functions rather than entire lodash library to minimize bundle size (e.g., import groupBy from 'lodash/groupBy')

Files:

  • apps/web/__tests__/e2e/cold-email/microsoft-cold-email.test.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/data-fetching.mdc)

**/*.{ts,tsx}: For API GET requests to server, use the swr package
Use result?.serverError with toastError from @/components/Toast for error handling in async operations

**/*.{ts,tsx}: Use wrapper functions for Gmail message operations (get, list, batch, etc.) from @/utils/gmail/message.ts instead of direct API calls
Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls
Use wrapper functions for Gmail label operations from @/utils/gmail/label.ts instead of direct API calls

**/*.{ts,tsx}: For early access feature flags, create hooks using the naming convention use[FeatureName]Enabled that return a boolean from useFeatureFlagEnabled("flag-key")
For A/B test variant flags, create hooks using the naming convention use[FeatureName]Variant that define variant types, use useFeatureFlagVariantKey() with type casting, and provide a default "control" fallback
Use kebab-case for PostHog feature flag keys (e.g., inbox-cleaner, pricing-options-2)
Always define types for A/B test variant flags (e.g., type PricingVariant = "control" | "variant-a" | "variant-b") and provide type safety through type casting

**/*.{ts,tsx}: Don't use primitive type aliases or misleading types
Don't use empty type parameters in type aliases and interfaces
Don't use this and super in static contexts
Don't use any or unknown as type constraints
Don't use the TypeScript directive @ts-ignore
Don't use TypeScript enums
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 TypeScript namespaces
Don't use non-null assertions with the ! postfix operator
Don't use parameter properties in class constructors
Don't use user-defined types
Use as const instead of literal types and type annotations
Use either T[] or Array<T> consistently
Initialize each enum member value explicitly
Use export type for types
Use `impo...

Files:

  • apps/web/__tests__/e2e/cold-email/microsoft-cold-email.test.ts
apps/web/__tests__/**/*.test.ts

📄 CodeRabbit inference engine (.cursor/rules/llm-test.mdc)

apps/web/__tests__/**/*.test.ts: Place all LLM-related tests in apps/web/__tests__/ directory
Use vitest imports (describe, expect, test, vi, beforeEach) in LLM test files
Mock 'server-only' module with empty object in LLM test files: vi.mock("server-only", () => ({}))
Set timeout constant const TIMEOUT = 15_000; for LLM tests
Use describe.runIf(isAiTest) with environment variable RUN_AI_TESTS === "true" to conditionally run LLM tests
Use console.debug() for outputting generated LLM content in tests, e.g., console.debug("Generated content:\n", result.content);
Prefer using existing helpers from @/__tests__/helpers.ts (getEmailAccount, getEmail, getRule, getMockMessage, getMockExecutedRule) instead of creating custom test data helpers

Files:

  • apps/web/__tests__/e2e/cold-email/microsoft-cold-email.test.ts
apps/web/{utils/ai,utils/llms,__tests__}/**/*.ts

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

LLM-related code must be organized in specific directories: apps/web/utils/ai/ for main implementations, apps/web/utils/llms/ for core utilities and configurations, and apps/web/__tests__/ for LLM-specific tests

Files:

  • apps/web/__tests__/e2e/cold-email/microsoft-cold-email.test.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/prisma-enum-imports.mdc)

Always import Prisma enums from @/generated/prisma/enums instead of @/generated/prisma/client to avoid Next.js bundling errors in client components

Import Prisma using the project's centralized utility: import prisma from '@/utils/prisma'

Files:

  • apps/web/__tests__/e2e/cold-email/microsoft-cold-email.test.ts
**/*.ts

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

**/*.ts: ALL database queries MUST be scoped to the authenticated user/account by including user/account filtering in WHERE clauses to prevent unauthorized data access
Always validate that resources belong to the authenticated user before performing operations, using ownership checks in WHERE clauses or relationships
Always validate all input parameters for type, format, and length before using them in database queries
Use SafeError for error responses to prevent information disclosure. Generic error messages should not reveal internal IDs, logic, or resource ownership details
Only return necessary fields in API responses using Prisma's select option. Never expose sensitive data such as password hashes, private keys, or system flags
Prevent Insecure Direct Object References (IDOR) by validating resource ownership before operations. All findUnique/findFirst calls MUST include ownership filters
Prevent mass assignment vulnerabilities by explicitly whitelisting allowed fields in update operations instead of accepting all user-provided data
Prevent privilege escalation by never allowing users to modify system fields, ownership fields, or admin-only attributes through user input
All findMany queries MUST be scoped to the user's data by including appropriate WHERE filters to prevent returning data from other users
Use Prisma relationships for access control by leveraging nested where clauses (e.g., emailAccount: { id: emailAccountId }) to validate ownership

Files:

  • apps/web/__tests__/e2e/cold-email/microsoft-cold-email.test.ts
**/*.test.{ts,tsx}

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

**/*.test.{ts,tsx}: Use vitest for testing the application
Tests should be colocated next to the tested file with .test.ts or .test.tsx extension (e.g., dir/format.ts and dir/format.test.ts)
Mock server-only using vi.mock("server-only", () => ({}))
Mock Prisma using vi.mock("@/utils/prisma") and import the mock from @/utils/__mocks__/prisma
Use vi.clearAllMocks() in beforeEach to clean up mocks between tests
Each test should be independent
Use descriptive test names
Mock external dependencies in tests
Do not mock the Logger
Avoid testing implementation details
Use test helpers getEmail, getEmailAccount, and getRule from @/__tests__/helpers for mocking emails, accounts, and rules

Files:

  • apps/web/__tests__/e2e/cold-email/microsoft-cold-email.test.ts
**/__tests__/**/*.{ts,tsx}

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

AI tests must be placed in the __tests__ directory and are not run by default (they use a real LLM)

Files:

  • apps/web/__tests__/e2e/cold-email/microsoft-cold-email.test.ts
**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)

**/*.{tsx,ts}: Use Shadcn UI and Tailwind for components and styling
Use next/image package for images
For API GET requests to server, use the swr package with hooks like useSWR to fetch data
For text inputs, use the Input component with registerProps for form integration and error handling

Files:

  • apps/web/__tests__/e2e/cold-email/microsoft-cold-email.test.ts
**/*.{tsx,ts,css}

📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)

Implement responsive design with Tailwind CSS using a mobile-first approach

Files:

  • apps/web/__tests__/e2e/cold-email/microsoft-cold-email.test.ts
**/*.{js,jsx,ts,tsx}

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

**/*.{js,jsx,ts,tsx}: Don't use accessKey attribute on any HTML element
Don't set aria-hidden="true" on focusable elements
Don't add ARIA roles, states, and properties to elements that don't support them
Don't use distracting elements like <marquee> or <blink>
Only use the scope prop on <th> elements
Don't assign non-interactive ARIA roles to interactive HTML elements
Make sure label elements have text content and are associated with an input
Don't assign interactive ARIA roles to non-interactive HTML elements
Don't assign tabIndex to non-interactive HTML elements
Don't use positive integers for tabIndex property
Don't include "image", "picture", or "photo" in img alt prop
Don't use explicit role property that's the same as the implicit/default role
Make static elements with click handlers use a valid role attribute
Always include a title element for SVG elements
Give all elements requiring alt text meaningful information for screen readers
Make sure anchors have content that's accessible to screen readers
Assign tabIndex to non-interactive HTML elements with aria-activedescendant
Include all required ARIA attributes for elements with ARIA roles
Make sure ARIA properties are valid for the element's supported roles
Always include a type attribute for button elements
Make elements with interactive roles and handlers focusable
Give heading elements content that's accessible to screen readers (not hidden with aria-hidden)
Always include a lang attribute on the html element
Always include a title attribute for iframe elements
Accompany onClick with at least one of: onKeyUp, onKeyDown, or onKeyPress
Accompany onMouseOver/onMouseOut with onFocus/onBlur
Include caption tracks for audio and video elements
Use semantic elements instead of role attributes in JSX
Make sure all anchors are valid and navigable
Ensure all ARIA properties (aria-*) are valid
Use valid, non-abstract ARIA roles for elements with ARIA roles
Use valid AR...

Files:

  • apps/web/__tests__/e2e/cold-email/microsoft-cold-email.test.ts
**/*.{test,spec}.{js,jsx,ts,tsx}

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

**/*.{test,spec}.{js,jsx,ts,tsx}: Don't nest describe() blocks too deeply in test files
Don't use callbacks in asynchronous tests and hooks
Don't have duplicate hooks in describe blocks
Don't use export or module.exports in test files
Don't use focused tests
Make sure the assertion function, like expect, is placed inside an it() function call
Don't use disabled tests

Files:

  • apps/web/__tests__/e2e/cold-email/microsoft-cold-email.test.ts
!(pages/_document).{jsx,tsx}

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

Don't use the next/head module in pages/_document.js on Next.js projects

Files:

  • apps/web/__tests__/e2e/cold-email/microsoft-cold-email.test.ts
**/*.{js,ts,jsx,tsx}

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

**/*.{js,ts,jsx,tsx}: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size (e.g., import groupBy from 'lodash/groupBy')

Files:

  • apps/web/__tests__/e2e/cold-email/microsoft-cold-email.test.ts
🧠 Learnings (3)
📓 Common learnings
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-25T14:40:00.833Z
Learning: Applies to **/*.test.{ts,tsx} : Use test helpers `getEmail`, `getEmailAccount`, and `getRule` from `@/__tests__/helpers` for mocking emails, accounts, and rules
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Prefer using existing helpers from `@/__tests__/helpers.ts` (`getEmailAccount`, `getEmail`, `getRule`, `getMockMessage`, `getMockExecutedRule`) instead of creating custom test data helpers
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Design Gmail wrapper functions to be provider-agnostic to support future email providers like Outlook and ProtonMail
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail message operations (get, list, batch, etc.) from @/utils/gmail/message.ts instead of direct API calls
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to apps/web/utils/gmail/**/*.{ts,tsx} : Keep Gmail provider-specific implementation details isolated within the apps/web/utils/gmail/ directory
📚 Learning: 2025-11-25T14:37:56.430Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Prefer using existing helpers from `@/__tests__/helpers.ts` (`getEmailAccount`, `getEmail`, `getRule`, `getMockMessage`, `getMockExecutedRule`) instead of creating custom test data helpers

Applied to files:

  • apps/web/__tests__/e2e/cold-email/microsoft-cold-email.test.ts
📚 Learning: 2025-11-25T14:40:00.833Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-25T14:40:00.833Z
Learning: Applies to **/*.test.{ts,tsx} : Use test helpers `getEmail`, `getEmailAccount`, and `getRule` from `@/__tests__/helpers` for mocking emails, accounts, and rules

Applied to files:

  • apps/web/__tests__/e2e/cold-email/microsoft-cold-email.test.ts
🧬 Code graph analysis (1)
apps/web/__tests__/e2e/cold-email/microsoft-cold-email.test.ts (2)
apps/web/utils/email/provider.ts (1)
  • createEmailProvider (14-32)
apps/web/utils/email.ts (2)
  • extractEmailAddress (19-52)
  • extractDomainFromEmail (73-91)
⏰ 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: test
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (1)
apps/web/__tests__/e2e/cold-email/microsoft-cold-email.test.ts (1)

139-308: LGTM! Comprehensive E2E test coverage.

The test suite provides excellent coverage of the hasPreviousCommunicationsWithSenderOrDomain functionality for Microsoft Outlook:

  • ✅ Properly uses skip parameter from test context for dynamic skipping (addresses the previous review comment)
  • ✅ Tests both received and sent email detection
  • ✅ Covers domain-based detection for company domains
  • ✅ Includes edge cases (unknown senders, date boundaries)
  • ✅ Clear console warnings when tests are skipped due to missing data
  • ✅ Appropriate for E2E testing with real inbox data

@elie222 elie222 merged commit e3c15c3 into main Dec 17, 2025
14 checks passed
@elie222 elie222 deleted the fix/outlook-query branch December 17, 2025 10:14
@coderabbitai coderabbitai bot mentioned this pull request Dec 25, 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