Skip to content

Filebot#1127

Closed
elie222 wants to merge 87 commits intomainfrom
feat/folders
Closed

Filebot#1127
elie222 wants to merge 87 commits intomainfrom
feat/folders

Conversation

@elie222
Copy link
Owner

@elie222 elie222 commented Dec 20, 2025

User description

Summary by CodeRabbit

  • New Features

    • Smart Filing: AI-powered automatic organization of email attachments into Google Drive & OneDrive with onboarding, setup, preview, and activity views
    • Drive integrations: connect Google Drive and OneDrive via OAuth and manage connections, folders, and filing rules in-app
    • Attachment preview and filing workflow with per-file status, notifications, and history
  • Improvements

    • UI: new MutedText, TruncatedText and Tree components; Smart Filing nav item and refined typography
    • Email providers enhanced to support attachment-based workflows
  • Documentation

    • Added NEXT_PUBLIC_SMART_FILING_ENABLED environment flag docs

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


Note

Smart Filing (Drive/OneDrive) feature

  • New /drive experience: onboarding, setup (folder selection + natural-language rules), preview with per-file feedback/move, preferences, and recent activity
  • Drive integrations: OAuth flows and callbacks for Google and Microsoft; connections list/manage; folder and subfolder browsing/creation
  • Backend APIs: connections, folders/subfolders, filing preview (messages/attachments), file actions (move, feedback), and filings listing; email account API now exposes filingEnabled/filingPrompt
  • Database: adds DriveConnection, FilingFolder, DocumentFiling (with preview/feedback fields), and new EmailAccount.filingEnabled/filingPrompt columns
  • UI components: tree view, filing status/yes-no indicator, table tooltip, updated Card (size prop), new MutedText; SideNav entry and feature flag/env NEXT_PUBLIC_SMART_FILING_ENABLED
  • Docs: README updated with Meeting Briefs (Beta) and Smart Filing, Drive/OneDrive redirect URIs, scopes, and APIs
  • Dependencies: add @googleapis/drive, mammoth, unpdf; register @kibo-ui

Written by Cursor Bugbot for commit c955629. Configure here.


Generated description

Below is a concise technical summary of the changes proposed in this PR:

graph LR
GOOGLEAPIS_DRIVE_CLIENT_("GOOGLEAPIS_DRIVE_CLIENT"):::added
GOOGLE_DRIVE_API_("GOOGLE_DRIVE_API"):::added
MICROSOFT_GRAPH_CLIENT_("MICROSOFT_GRAPH_CLIENT"):::added
MICROSOFT_GRAPH_API_("MICROSOFT_GRAPH_API"):::modified
GOOGLE_OAUTH_("GOOGLE_OAUTH"):::added
PRISMA_DB_("PRISMA_DB"):::modified
MICROSOFT_OAUTH_("MICROSOFT_OAUTH"):::added
REDIS_("REDIS"):::modified
GOOGLEAPIS_DRIVE_CLIENT_ -- "Sends attachment upload and folder-listing requests for previews." --> GOOGLE_DRIVE_API_
GOOGLE_DRIVE_API_ -- "Returns folder listings and upload confirmations to client." --> GOOGLEAPIS_DRIVE_CLIENT_
MICROSOFT_GRAPH_CLIENT_ -- "Requests OneDrive folder listings and file uploads for auto-filing." --> MICROSOFT_GRAPH_API_
MICROSOFT_GRAPH_API_ -- "Provides folder/file metadata and upload/move responses." --> MICROSOFT_GRAPH_CLIENT_
GOOGLE_OAUTH_ -- "Stores Google access/refresh tokens in Prisma driveConnection." --> PRISMA_DB_
MICROSOFT_OAUTH_ -- "Persists Microsoft OAuth tokens to Prisma driveConnection records." --> PRISMA_DB_
GOOGLE_OAUTH_ -- "Caches OAuth code results and acquires locks to deduplicate." --> REDIS_
MICROSOFT_OAUTH_ -- "Uses Redis lock/cache to prevent duplicate OAuth processing." --> REDIS_
classDef added stroke:#15AA7A
classDef removed stroke:#CD5270
classDef modified stroke:#EDAC4C
linkStyle default stroke:#CBD5E1,font-size:13px
Loading

Implements a new AI-powered Smart Filing feature, enabling automatic organization of email attachments into Google Drive and OneDrive. Integrates OAuth flows for secure drive connections and provides user interfaces for setup, folder selection, filing preview, and activity tracking.

TopicDetails
UI Refactoring Refactors UI components to use a new MutedText component for consistent styling of muted text and introduces a size prop for Card components to allow for more compact layouts.
Modified files (31)
  • apps/web/app/(app)/[emailAccountId]/assistant/ActionSteps.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ProcessRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ResultDisplay.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/Rules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/group/ViewLearnedPatterns.tsx
  • apps/web/app/(app)/[emailAccountId]/clean/CleanHistory.tsx
  • apps/web/app/(app)/[emailAccountId]/clean/ConfirmationStep.tsx
  • apps/web/app/(app)/[emailAccountId]/debug/rule-history/[ruleId]/page.tsx
  • apps/web/app/(app)/[emailAccountId]/integrations/IntegrationRow.tsx
  • apps/web/app/(app)/[emailAccountId]/onboarding/OnboardingCategories.tsx
  • apps/web/app/(app)/[emailAccountId]/onboarding/StepFeatures.tsx
  • apps/web/app/(app)/[emailAccountId]/onboarding/StepIntro.tsx
  • apps/web/app/(app)/[emailAccountId]/onboarding/StepWho.tsx
  • apps/web/app/(app)/[emailAccountId]/reply-zero/ReplyTrackerEmails.tsx
  • apps/web/app/(app)/[emailAccountId]/setup/SetupContent.tsx
  • apps/web/app/(app)/accounts/AddAccount.tsx
  • apps/web/app/(app)/accounts/CopyRulesDialog.tsx
  • apps/web/app/(app)/organization/[organizationId]/stats/OrgStats.tsx
  • apps/web/app/(landing)/components/page.tsx
  • apps/web/app/(landing)/login/page.tsx
  • apps/web/components/EmailViewer.tsx
  • apps/web/components/PremiumCard.tsx
  • apps/web/components/SettingCard.tsx
  • apps/web/components/StatsCards.tsx
  • apps/web/components/Typography.tsx
  • apps/web/components/VideoCard.tsx
  • apps/web/components/WebhookDocumentation.tsx
  • apps/web/components/email-list/EmailMessage.tsx
  • apps/web/components/email-list/EmailPanel.tsx
  • apps/web/components/ui/card.tsx
Latest Contributors(2)
UserCommitDate
elie222Move-bulk-process-butt...January 05, 2026
joshwerner001@gmail.comFix-issue-when-removin...December 04, 2025
Calendar Error Handling Refactors calendar connection logic to use a shared captureException utility for error reporting and removes direct logger calls for error initiation, improving consistency in error handling.
Modified files (2)
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsx
Latest Contributors(2)
UserCommitDate
elie222security-add-Secure-at...January 06, 2026
eduardoleliss@gmail.comPR-feedbackOctober 10, 2025
Remove StatsCard Removes the StatsCard.tsx component, indicating a consolidation or removal of a specific statistics display component.
Modified files (1)
  • apps/web/components/StatsCard.tsx
Latest Contributors(1)
UserCommitDate
joshwerner001@gmail.comUpdate-keyNovember 25, 2025
Smart Filing Introduces AI-powered automatic organization of email attachments into Google Drive and OneDrive, including OAuth-based drive integrations, a comprehensive setup wizard for defining filing rules and selecting folders, and user interfaces for previewing filings and tracking activity. Enhances email providers to support attachment-based workflows and adds new database models for DriveConnection, FilingFolder, and DocumentFiling.
Modified files (89)
  • README.md
  • apps/web/app/(app)/(redirects)/drive/page.tsx
  • apps/web/app/(app)/[emailAccountId]/drive/AllowedFolders.tsx
  • apps/web/app/(app)/[emailAccountId]/drive/ConnectDrive.tsx
  • apps/web/app/(app)/[emailAccountId]/drive/DriveConnectionCard.tsx
  • apps/web/app/(app)/[emailAccountId]/drive/DriveConnections.tsx
  • apps/web/app/(app)/[emailAccountId]/drive/DriveOnboarding.tsx
  • apps/web/app/(app)/[emailAccountId]/drive/DriveSetup.tsx
  • apps/web/app/(app)/[emailAccountId]/drive/FilingActivity.tsx
  • apps/web/app/(app)/[emailAccountId]/drive/FilingPreferences.tsx
  • apps/web/app/(app)/[emailAccountId]/drive/FilingRulesForm.tsx
  • apps/web/app/(app)/[emailAccountId]/drive/page.tsx
  • apps/web/app/api/google/drive/auth-url/route.ts
  • apps/web/app/api/google/drive/callback/route.ts
  • apps/web/app/api/google/webhook/types.ts
  • apps/web/app/api/outlook/drive/auth-url/route.ts
  • apps/web/app/api/outlook/drive/callback/route.ts
  • apps/web/app/api/user/drive/connections/route.ts
  • apps/web/app/api/user/drive/filings/route.ts
  • apps/web/app/api/user/drive/folders/[folderId]/route.ts
  • apps/web/app/api/user/drive/folders/route.ts
  • apps/web/app/api/user/drive/preview/attachments/route.ts
  • apps/web/app/api/user/drive/preview/route.ts
  • apps/web/app/api/user/email-account/route.ts
  • apps/web/components.json
  • apps/web/components/SideNav.tsx
  • apps/web/components/TruncatedText.tsx
  • apps/web/components/drive/FilingStatusCell.tsx
  • apps/web/components/drive/TableCellWithTooltip.tsx
  • apps/web/components/drive/YesNoIndicator.tsx
  • apps/web/components/kibo-ui/tree/index.tsx
  • apps/web/env.ts
  • apps/web/hooks/useDriveConnections.ts
  • apps/web/hooks/useDriveFolders.ts
  • apps/web/hooks/useDriveSubfolders.ts
  • apps/web/hooks/useFeatureFlags.ts
  • apps/web/hooks/useFilingActivity.ts
  • apps/web/hooks/useFilingPreview.ts
  • apps/web/hooks/useFilingPreviewAttachments.ts
  • apps/web/package.json
  • apps/web/prisma/migrations/20251221132935_drive/migration.sql
  • apps/web/prisma/migrations/20251222222738_add_filing_preview_support/migration.sql
  • apps/web/prisma/migrations/20251223000001_rename_notification_token_to_message_id/migration.sql
  • apps/web/prisma/schema.prisma
  • apps/web/utils/actions/drive.ts
  • apps/web/utils/actions/drive.validation.ts
  • apps/web/utils/ai/document-filing/analyze-document.ts
  • apps/web/utils/calendar/handle-calendar-callback.ts
  • apps/web/utils/calendar/oauth-callback-helpers.ts
  • apps/web/utils/drive/client.ts
  • apps/web/utils/drive/constants.ts
  • apps/web/utils/drive/document-extraction.test.ts
  • apps/web/utils/drive/document-extraction.ts
  • apps/web/utils/drive/filing-engine.ts
  • apps/web/utils/drive/filing-notifications.ts
  • apps/web/utils/drive/folder-utils.test.ts
  • apps/web/utils/drive/folder-utils.ts
  • apps/web/utils/drive/handle-drive-callback.ts
  • apps/web/utils/drive/handle-filing-reply.test.ts
  • apps/web/utils/drive/handle-filing-reply.ts
  • apps/web/utils/drive/provider.ts
  • apps/web/utils/drive/providers/google-token.ts
  • apps/web/utils/drive/providers/google.ts
  • apps/web/utils/drive/providers/microsoft-token.ts
  • apps/web/utils/drive/providers/microsoft.ts
  • apps/web/utils/drive/providers/token-helpers.ts
  • apps/web/utils/drive/scopes.ts
  • apps/web/utils/drive/types.ts
  • apps/web/utils/drive/url.test.ts
  • apps/web/utils/drive/url.ts
  • apps/web/utils/email/google.ts
  • apps/web/utils/email/microsoft.ts
  • apps/web/utils/email/types.ts
  • apps/web/utils/filebot/is-filebot-email.test.ts
  • apps/web/utils/filebot/is-filebot-email.ts
  • apps/web/utils/oauth/redirect.ts
  • apps/web/utils/oauth/verify.test.ts
  • apps/web/utils/oauth/verify.ts
  • apps/web/utils/outlook/draft.ts
  • apps/web/utils/outlook/errors.test.ts
  • apps/web/utils/outlook/errors.ts
  • apps/web/utils/outlook/message.ts
  • apps/web/utils/prisma-extensions.ts
  • apps/web/utils/redis/oauth-code.ts
  • apps/web/utils/webhook/process-history-item.ts
  • apps/web/utils/webhook/validate-webhook-account.ts
  • docs/hosting/environment-variables.md
  • package.json
  • pnpm-lock.yaml
Checks (1)
  • Run Tests / test
Latest Contributors(2)
UserCommitDate
rsnodgrass@gmail.comfeat-add-devcontainer-...January 04, 2026
elie222adjust-uiDecember 31, 2025
This pull request is reviewed by Baz. Review like a pro on (Baz).

@vercel
Copy link

vercel bot commented Dec 20, 2025

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

Project Deployment Review Updated (UTC)
inbox-zero Ready Ready Preview Jan 12, 2026 6:12pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 20, 2025

📝 Walkthrough

Walkthrough

Adds a Smart Filing feature: Drive OAuth flows (Google/OneDrive), drive providers, document extraction, AI-driven filing pipeline, DB models/migrations, server actions and APIs, extensive UI for setup/preview/activity, background webhook processing, token encryption, and tests.

Changes

Cohort / File(s) Summary
Drive OAuth & Client Utilities
apps/web/utils/drive/client.ts, apps/web/utils/drive/constants.ts, apps/web/utils/drive/scopes.ts, apps/web/utils/drive/types.ts
OAuth URL generation and code exchange helpers for Google and Microsoft, scope constants, DRIVE_STATE cookie constant, and core drive types.
Drive Callback / Connection Management
apps/web/utils/drive/handle-drive-callback.ts, apps/web/utils/drive/provider.ts, apps/web/app/api/google/drive/{auth-url,callback}/route.ts, apps/web/app/api/outlook/drive/{auth-url,callback}/route.ts, apps/web/app/api/user/drive/connections/route.ts
Unified callback handler, token refresh/persistence, Redis dedupe lock, routes to initiate callbacks and list connections.
Drive Providers & Token Helpers
apps/web/utils/drive/providers/*.ts, apps/web/utils/drive/providers/token-helpers.ts, apps/web/utils/drive/providers/google-token.ts, apps/web/utils/drive/providers/microsoft-token.ts
Provider implementations (Google/OneDrive) for folder/file ops, token refresh flows, and token save/disconnect helpers.
Document Extraction & Analysis (LLM)
apps/web/utils/drive/document-extraction.ts, apps/web/utils/ai/document-filing/analyze-document.ts
Lazy PDF/DOCX extraction, cleaning, preview helpers, and LLM-based document analysis producing folder/action/confidence results.
Filing Engine & Reply Handling
apps/web/utils/drive/filing-engine.ts, apps/web/utils/drive/handle-filing-reply.ts, apps/web/utils/drive/folder-utils.ts, apps/web/utils/drive/filing-notifications.ts, apps/web/utils/drive/filing-*.ts
End-to-end filing pipeline: extract, analyze, create folders, upload files, persist DocumentFiling, notifications, and reply-driven corrections.
Server Actions & Validation
apps/web/utils/actions/drive.ts, apps/web/utils/actions/drive.validation.ts
Server actions for disconnecting, toggling filing, managing folders, moving files, filing attachments, with Zod schemas/typed bodies.
APIs for Previews, Folders, Filings
apps/web/app/api/user/drive/{folders,folders/[folderId],filings,preview,preview/attachments}/route.ts
Endpoints for available/saved folders, subfolders, paginated filings, filing preview and attachment previews, with typed responses and error handling.
Hooks & Client Data Fetching
apps/web/hooks/{useDriveConnections,useDriveFolders,useDriveSubfolders,useFilingPreview,useFilingPreviewAttachments,useFilingActivity}.ts
SWR hooks for drive connections, folder lists/subfolders, preview data, and filing activity.
UI: Drive Setup / Onboarding / Settings
apps/web/app/(app)/[emailAccountId]/drive/{page,DriveOnboarding,DriveSetup,DriveConnections,DriveConnectionCard,ConnectDrive,FilingPreferences,FilingRulesForm,AllowedFolders,FilingActivity,FilingStatusCell,TruncatedText,TableCellWithTooltip,YesNoIndicator}.tsx
Multi-view UI for onboarding, setup, folder selection, rules, connections, previews, activity, and small UI primitives for status and truncated text.
Prisma Schema, Migrations & Extensions
apps/web/prisma/schema.prisma, apps/web/prisma/migrations/..._drive/*.sql, apps/web/prisma/migrations/..._filing_preview/*.sql, apps/web/utils/prisma-extensions.ts
New models DriveConnection, FilingFolder, DocumentFiling, enum DocumentFilingStatus, added EmailAccount fields, migrations, and encrypted token handling in Prisma extensions.
Webhook & Email Provider Changes
apps/web/utils/webhook/process-history-item.ts, apps/web/app/api/google/webhook/types.ts, apps/web/utils/outlook/{message,draft,errors}.ts, apps/web/utils/email/{google,microsoft,types}.ts
Filebot detection path, background attachment processing, added getMessagesWithAttachments to providers, Outlook error helpers.
Feature Flags, Env & SideNav
apps/web/env.ts, apps/web/hooks/useFeatureFlags.ts, apps/web/components/SideNav.tsx
NEXT_PUBLIC_SMART_FILING_ENABLED env var and useSmartFilingEnabled hook; SideNav entry for Smart Filing gated by flag.
UI Typography Consolidation
apps/web/components/Typography.tsx, many files in apps/web/app and apps/web/components
Adds MutedText and replaces ~40+ inline muted text usages across the app for consistent styling.
Dependencies & Config
apps/web/package.json, package.json, apps/web/components.json, README.md, docs/hosting/environment-variables.md
Adds drive-related deps (@googleapis/drive, mammoth, unpdf), bumps turbo, adds registry entry, README and env docs updated.
Tests & Coverage
apps/web/utils/drive/{folder-utils,document-extraction,url}.test.ts, apps/web/utils/filebot/is-filebot-email.test.ts, apps/web/utils/drive/handle-filing-reply.test.ts, apps/web/utils/oauth/verify.test.ts, apps/web/utils/outlook/errors.test.ts
New unit tests covering extraction, folder utils, URL helper, filebot email parsing, filing-reply parsing, OAuth verify, and Outlook error helpers.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Browser
    participant AuthAPI as /api/{google,outlook}/drive/auth-url
    participant OAuthProvider as Google/Microsoft
    participant CallbackAPI as /api/{google,outlook}/drive/callback
    participant Redis as Redis (lock)
    participant DB as Database
    participant BrowserUI as Browser (redirect)

    User->>Browser: Click "Connect Drive"
    Browser->>AuthAPI: GET auth-url
    AuthAPI->>AuthAPI: generate state cookie
    AuthAPI-->>Browser: { url }
    Browser->>OAuthProvider: redirect to consent
    OAuthProvider-->>CallbackAPI: redirect with code & state
    CallbackAPI->>Redis: acquire lock(code)
    CallbackAPI->>CallbackAPI: validate state, exchange code
    CallbackAPI->>DB: upsert DriveConnection (encrypt tokens)
    CallbackAPI->>Redis: release lock
    CallbackAPI-->>BrowserUI: redirect success
    BrowserUI->>User: "Drive connected"
Loading
sequenceDiagram
    participant Webhook
    participant EmailProvider
    participant FilingEngine
    participant Extraction as Extractor
    participant LLM
    participant DriveProvider
    participant DB
    participant Notification

    Webhook->>EmailProvider: fetch message with attachment
    EmailProvider-->>FilingEngine: message + attachment
    FilingEngine->>Extraction: extract text
    Extraction-->>FilingEngine: extracted text
    FilingEngine->>LLM: analyze document -> action/folder/confidence
    alt High confidence
        FilingEngine->>DriveProvider: create folders & upload file
        DriveProvider-->>FilingEngine: fileId
        FilingEngine->>DB: create DocumentFiling(status=FILED)
        FilingEngine->>Notification: send "Filed" email
    else Low confidence
        FilingEngine->>DB: create DocumentFiling(status=PENDING)
        FilingEngine->>Notification: send "Ask" email (filebot reply-to)
    end
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Suggested reviewers

  • baz-reviewer
  • edulelis

"🐰
I hopped through code with eager paws,
Connected drives and healed the flaws,
Papers tucked in folders neat and smart—
A rabbit's work: automated art! 📂✨"

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.29% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'Filebot' is vague and generic, using a single-word product/feature name without context. It does not clearly convey the main change—implementing a Smart Filing feature for organizing email attachments into Google Drive and OneDrive. Rename the title to something more descriptive, such as 'Add Smart Filing feature for Google Drive and OneDrive integration' or 'Implement automatic document organization via Drive/OneDrive' to clearly communicate the primary change.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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
Contributor

macroscopeapp bot commented Dec 20, 2025

Add Smart Filing with Google Drive and OneDrive, including OAuth flows, token refresh, document extraction limits (PDF ≤32MB and ≤100 pages), preview APIs, and a drive settings UI in apps/web/app/(app)/[emailAccountId]/drive/page.tsx

Introduce drive providers (GoogleDriveProvider, OneDriveProvider) with token refresh and folder/file operations, unified OAuth callbacks and auth-url endpoints, document text extraction and filing analysis, preview/attachments/activity APIs, server actions for folders and filing, Prisma models/migrations for drive entities and filings, and UI pages/components for connecting drives, configuring folders/rules, and viewing activity.

📍Where to Start

Start with the unified drive OAuth callback handler handleDriveCallback in apps/web/utils/drive/handle-drive-callback.ts, then review provider factories in apps/web/utils/drive/provider.ts and the main drive page in apps/web/app/(app)/[emailAccountId]/drive/page.tsx.


Macroscope summarized 7270769.

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: 9

🧹 Nitpick comments (15)
apps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsx (1)

33-35: Suggest enriching error context with emailAccountId.

The error handling correctly uses captureException with descriptive context. However, since emailAccountId is already available in the component scope and captureException accepts it in the context parameter, consider including it for better debugging capability in production.

🔎 Proposed enhancement

For the Google Calendar handler:

-      captureException(error, {
-        extra: { context: "Google Calendar OAuth initiation" },
-      });
+      captureException(error, {
+        emailAccountId,
+        extra: { context: "Google Calendar OAuth initiation" },
+      });

For the Microsoft Calendar handler:

-      captureException(error, {
-        extra: { context: "Microsoft Calendar OAuth initiation" },
-      });
+      captureException(error, {
+        emailAccountId,
+        extra: { context: "Microsoft Calendar OAuth initiation" },
+      });

Also applies to: 60-62

apps/web/utils/ai/document-filing/analyze-document.ts (3)

1-5: Missing scoped logger for LLM feature.

Per coding guidelines for LLM features, this function should use createScopedLogger from @/utils/logger for logging inputs and outputs with appropriate log levels. Based on learnings, LLM feature functions must import and use descriptive scoped loggers.

🔎 Suggested fix
 import { z } from "zod";
 import type { EmailAccountWithAI } from "@/utils/llms/types";
 import { getModel } from "@/utils/llms/model";
 import { createGenerateObject } from "@/utils/llms";
 import { cleanExtractedText } from "@/utils/drive/document-extraction";
+import { createScopedLogger } from "@/utils/logger";
+
+const logger = createScopedLogger("document-filing");

43-70: Consider adding input validation with early returns.

Per coding guidelines for LLM features, functions should implement input validation with early returns for invalid inputs. This prevents wasted API calls and provides better error handling.

🔎 Suggested validation
 export async function analyzeDocument({
   emailAccount,
   email,
   attachment,
   folders,
 }: {
   emailAccount: EmailAccountWithAI & { filingPrompt: string };
   email: EmailContext;
   attachment: AttachmentContext;
   folders: DriveFolder[];
 }): Promise<DocumentAnalysisResult> {
+  if (!attachment.content?.trim()) {
+    logger.warn("Empty attachment content, skipping analysis");
+    return {
+      action: "use_existing" as const,
+      confidence: 0,
+      reasoning: "No document content to analyze",
+    };
+  }
+
+  logger.debug("Analyzing document", {
+    filename: attachment.filename,
+    subject: email.subject,
+    folderCount: folders.length,
+  });
+
   const modelOptions = getModel(emailAccount.user, "economy");

7-31: Consider adding Zod refinement for conditional field validation.

The schema allows invalid states where action: "use_existing" could have no folderId, or action: "create_new" could have no folderPath. A Zod refinement would enforce these constraints at runtime.

🔎 Suggested refinement
 const documentAnalysisSchema = z.object({
   action: z
     .enum(["use_existing", "create_new"])
     .describe("Whether to use an existing folder or create a new one."),
   folderId: z
     .string()
     .optional()
     .describe(
       "Required if action is 'use_existing'. The ID of the existing folder from the provided list.",
     ),
   folderPath: z
     .string()
     .optional()
     .describe(
       "Required if action is 'create_new'. The path for the new folder to create.",
     ),
   confidence: z
     .number()
     .min(0)
     .max(1)
     .describe("Confidence score from 0 to 1. Use 0.9+ only when very certain."),
   reasoning: z
     .string()
     .describe("Brief explanation for why this folder was chosen."),
-});
+}).refine(
+  (data) => {
+    if (data.action === "use_existing") return !!data.folderId;
+    if (data.action === "create_new") return !!data.folderPath;
+    return true;
+  },
+  {
+    message: "folderId required for use_existing, folderPath required for create_new",
+  },
+);
apps/web/app/(app)/[emailAccountId]/drive/ConnectDrive.tsx (1)

17-42: Remove unnecessary Content-Type header for GET requests.

The Content-Type header on lines 23 and 50 is not needed for GET requests, as they don't send a request body. This applies to both the Google and Microsoft connection handlers.

🔎 Proposed fix
     const response = await fetchWithAccount({
       url: "/api/google/drive/auth-url",
       emailAccountId,
-      init: { headers: { "Content-Type": "application/json" } },
     });

Apply the same change to the Microsoft handler on line 50.

apps/web/utils/drive/provider.ts (1)

39-40: TODO reminder: Add token refresh support.

The TODO indicates future work for handling token expiration in long-running operations. Consider tracking this as a follow-up task if not already planned.

Would you like me to open an issue to track this enhancement?

apps/web/utils/actions/drive.ts (1)

8-30: Consider defense-in-depth for the delete operation.

The current implementation validates ownership with findFirst before deleting, which is correct. However, the delete operation only uses connectionId in the where clause (line 25).

For defense-in-depth against potential race conditions, consider including emailAccountId in the delete where clause:

🔎 Suggested improvement
       await prisma.driveConnection.delete({
-        where: { id: connectionId },
+        where: { 
+          id: connectionId,
+          emailAccountId,
+        },
       });

Note: Prisma may require a compound unique constraint for this syntax. Alternatively, you could use deleteMany which accepts multiple conditions without requiring unique constraints.

apps/web/app/(app)/[emailAccountId]/drive/DriveConnectionCard.tsx (1)

50-55: Consider awaiting the disconnect action before revalidating.

The current implementation doesn't wait for the disconnect action to complete before calling mutate(). This could cause a race condition where the UI refreshes before the server has finished deleting the connection.

🔎 Suggested improvement
   const handleDisconnect = async () => {
     if (confirm("Are you sure you want to disconnect this drive?")) {
-      executeDisconnect({ connectionId: connection.id });
-      mutate();
+      const result = await executeDisconnect({ connectionId: connection.id });
+      if (result?.data?.success) {
+        mutate();
+      }
     }
   };

This ensures the mutation only triggers after successful deletion and provides an opportunity to handle errors gracefully.

apps/web/utils/drive/client.ts (1)

145-152: Consider defining a type for the Microsoft token response.

The tokens object from the JSON response is untyped, requiring type assertions on each property. A typed interface would improve type safety and maintainability.

🔎 Suggested type definition
interface MicrosoftTokenResponse {
  access_token: string;
  refresh_token: string;
  expires_in?: number;
  token_type: string;
  scope: string;
}
apps/web/utils/drive/oauth-callback-helpers.ts (1)

16-26: LGTM!

Clean type definitions for the OAuth flow. Note that DriveOAuthState is also defined in apps/web/utils/drive/types.ts - consider importing from there to avoid duplication.

apps/web/utils/drive/providers/microsoft.ts (3)

22-34: Logger .with() usage at constructor level conflicts with coding guidelines.

Per coding guidelines, .with() should only be used within specific functions for context enrichment, not for global/file-level loggers. The logger instance here is stored at the class level.

Consider passing context directly in each log call instead:

🔎 Suggested refactor
  constructor(accessToken: string, logger?: Logger) {
    this.accessToken = accessToken;
-   this.logger = (logger || createScopedLogger("onedrive-provider")).with({
-     provider: "microsoft",
-   });
+   this.logger = logger || createScopedLogger("onedrive-provider");

    this.client = Client.init({
      authProvider: (done) => {
        done(null, this.accessToken);
      },
      defaultVersion: "v1.0",
    });
  }

Then include provider: "microsoft" in individual log calls where needed.


132-163: Consider tracking the large file upload limitation.

The 4MB limit and TODO for resumable upload are acknowledged. Would you like me to open an issue to track implementing resumable upload for large files?


194-218: Non-null assertions on item.id could fail for edge cases.

Both convertToFolder (line 196) and convertToFile (line 208) use item.id! which assumes id is always present. While Graph API typically returns IDs, a defensive check would be safer.

🔎 Suggested defensive approach
  private convertToFolder(item: DriveItem): DriveFolder {
+   if (!item.id) {
+     throw new Error("DriveItem missing required id field");
+   }
    return {
-     id: item.id!,
+     id: item.id,
      name: item.name || "Untitled",
      parentId: item.parentReference?.id ?? undefined,
      path: item.parentReference?.path
        ? `${item.parentReference.path}/${item.name}`
        : undefined,
      webUrl: item.webUrl ?? undefined,
    };
  }

Apply the same pattern to convertToFile.

apps/web/utils/drive/providers/google.ts (2)

26-41: Logger .with() usage at constructor level.

Same as the OneDrive provider - per coding guidelines, .with() should only be used within specific functions for context enrichment, not for class-level loggers.


196-217: Non-null assertions on file.id could fail for edge cases.

Same as the OneDrive provider - both convertToFolder (line 198) and convertToFile (line 209) use file.id!. Consider adding defensive checks.

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 (changes from recent commits).

Prompt for AI agents (all issues)

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


<file name="apps/web/utils/drive/document-extraction.ts">

<violation number="1" location="apps/web/utils/drive/document-extraction.ts:40">
P2: The `application/msword` MIME type is listed in `EXTRACTABLE_MIME_TYPES` but has no corresponding handler in the switch statement, causing `isExtractableMimeType()` to return `true` while `extractTextFromDocument()` returns `null`. Either remove `.doc` from the supported types or add explicit handling.</violation>
</file>

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

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.

3 issues found across 32 files

Prompt for AI agents (all issues)

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


<file name="apps/web/utils/drive/handle-drive-callback.ts">

<violation number="1" location="apps/web/utils/drive/handle-drive-callback.ts:137">
P1: The `await clearOAuthCode(code)` call should not mask the original error if it fails. Add `.catch()` to prevent cleanup failures from overriding the actual error handling.</violation>
</file>

<file name="apps/web/utils/ai/document-filing/analyze-document.ts">

<violation number="1" location="apps/web/utils/ai/document-filing/analyze-document.ts:7">
P2: The Zod schema doesn&#39;t enforce that `folderId` is required when `action` is `&#39;use_existing&#39;` and `folderPath` is required when `action` is `&#39;create_new&#39;`. Consider using `.refine()` to add conditional validation, otherwise the LLM might return invalid combinations.</violation>
</file>

<file name="apps/web/utils/drive/oauth-callback-helpers.ts">

<violation number="1" location="apps/web/utils/drive/oauth-callback-helpers.ts:148">
P2: Race condition: Use Prisma&#39;s `upsert` instead of `findFirst` + `update`/`create` pattern. The schema has a unique constraint on `[emailAccountId, provider]`, so `upsert` can atomically handle this in a single query, avoiding race conditions and duplicate records.</violation>
</file>

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

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: 3

♻️ Duplicate comments (3)
apps/web/utils/ai/document-filing/analyze-document.ts (2)

35-45: LGTM!

The refinement correctly enforces conditional requirements, addressing previous concerns about invalid state combinations.


103-145: Escape user data before interpolating into XML tags.

Special characters (<, >, &, quotes) in filename, subject, sender, and folder attributes can break XML structure, enabling prompt injection. Escape these values or use a different delimiter strategy.

🔎 Proposed XML escaping utility
+function escapeXml(unsafe: string): string {
+  return unsafe
+    .replace(/&/g, "&amp;")
+    .replace(/</g, "&lt;")
+    .replace(/>/g, "&gt;")
+    .replace(/"/g, "&quot;")
+    .replace(/'/g, "&apos;");
+}
+
 function buildPrompt({
   email,
   attachment,
   folders,
 }: {
   email: EmailContext;
   attachment: AttachmentContext;
   folders: DriveFolder[];
 }): string {
   const cleanedText = cleanExtractedText(attachment.content);
   const truncatedText =
     cleanedText.length > 8000
       ? `${cleanedText.slice(0, 8000)}\n\n[... document truncated ...]`
       : cleanedText;
 
   const foldersText =
     folders.length > 0
       ? folders
           .map(
             (f) =>
-              `<folder id="${f.id}" path="${f.path}" provider="${f.driveProvider}" />`,
+              `<folder id="${escapeXml(f.id)}" path="${escapeXml(f.path)}" provider="${escapeXml(f.driveProvider)}" />`,
           )
           .join("\n")
       : "No existing folders found.";
 
   return `Decide where to file this document:
 
 <document_metadata>
-<filename>${attachment.filename}</filename>
-<email_subject>${email.subject}</email_subject>
-<email_sender>${email.sender}</email_sender>
+<filename>${escapeXml(attachment.filename)}</filename>
+<email_subject>${escapeXml(email.subject)}</email_subject>
+<email_sender>${escapeXml(email.sender)}</email_sender>
 </document_metadata>
 
 <document_content>
 ${truncatedText}
 </document_content>
 
 <existing_folders>
 ${foldersText}
 </existing_folders>
 
 Based on the user's filing preferences and the document content, decide where this document should be filed.`;
 }

As per coding guidelines for LLM prompts, structure data safely to prevent injection.

apps/web/utils/drive/client.ts (1)

80-86: Add prompt: "consent" to Microsoft OAuth URL.

Without prompt: "consent", re-authorization may not return a refresh token, causing exchangeMicrosoftDriveCode to throw an error at Line 125. This is critical for refresh token flows.

🔎 Proposed fix
   const params = new URLSearchParams({
     client_id: env.MICROSOFT_CLIENT_ID,
     response_type: "code",
     redirect_uri: `${env.NEXT_PUBLIC_BASE_URL}/api/outlook/drive/callback`,
     scope: MICROSOFT_DRIVE_SCOPES.join(" "),
+    prompt: "consent",
     state,
   });
🧹 Nitpick comments (4)
apps/web/utils/redis/oauth-code.ts (1)

55-66: Well-documented silent error handling for cleanup operations.

The implementation correctly handles cleanup errors silently, which is appropriate for this use case. The JSDoc and inline comments clearly explain the design intent.

If you want to aid debugging without changing the fail-silently behavior, you could optionally accept a logger parameter to record cleanup failures while still preventing them from bubbling up:

🔎 Optional enhancement to log silent failures
-export async function clearOAuthCode(code: string): Promise<void> {
+import type { Logger } from "next-axiom";
+
+export async function clearOAuthCode(
+  code: string, 
+  logger?: Logger
+): Promise<void> {
   try {
     await redis.del(getCodeKey(code));
-  } catch {
+  } catch (error) {
     // Silently ignore - this is called in error handlers where we don't want
     // cleanup failures to mask the original error
+    logger?.error("Failed to clear OAuth code from Redis", { error });
   }
 }
apps/web/utils/filebot/is-filebot-email.ts (1)

44-48: Validation properly implemented.

The function now correctly validates userEmail format and throws a descriptive error for invalid input, addressing previous concerns.

Consider adding validation for the token parameter to ensure it matches the expected format [a-zA-Z0-9]+. This would catch potential issues early if the token is malformed.

🔎 Optional: Add token validation
 export function getFilebotEmail({
   userEmail,
   token,
 }: {
   userEmail: string;
   token: string;
 }): string {
   const [localPart, domain] = userEmail.split("@");
   if (!localPart || !domain) {
     throw new Error("Invalid email format");
   }
+  if (!token || !/^[a-zA-Z0-9]+$/.test(token)) {
+    throw new Error("Invalid token format");
+  }
   return `${localPart}+${FILEBOT_PREFIX}-${token}@${domain}`;
 }
apps/web/utils/drive/oauth-callback-helpers.ts (1)

61-68: Consider timing-safe comparison for state validation.

While unlikely to be exploited in this context, security best practices recommend using a timing-safe comparison for OAuth state tokens to prevent timing attacks.

🔎 Proposed enhancement
import { timingSafeEqual } from "crypto";

// In validateOAuthCallback:
const stateMatches = storedState && receivedState && 
  storedState.length === receivedState.length &&
  timingSafeEqual(Buffer.from(storedState), Buffer.from(receivedState));

if (!storedState || !receivedState || !stateMatches) {
  logger.warn("Invalid state during drive callback", {
    receivedState,
    hasStoredState: !!storedState,
  });
  redirectUrl.searchParams.set("error", "invalid_state");
  throw new RedirectError(redirectUrl, response.headers);
}
apps/web/utils/drive/document-extraction.ts (1)

117-119: Remove @ts-expect-error and use direct import for unpdf instead.

The unpdf package includes proper TypeScript type definitions with "./dist/index.d.ts". Rather than relying on dynamic import with type suppression, use a direct import of getDocumentProxy from unpdf like other functions in the codebase. If the package is not installed, install it first, then replace the dynamic import and @ts-expect-error comment with a standard import to leverage the library's native type support.

📜 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 c9589b4 and 9fe0ac1.

📒 Files selected for processing (8)
  • apps/web/app/(app)/[emailAccountId]/drive/DriveConnectionCard.tsx (1 hunks)
  • apps/web/utils/ai/document-filing/analyze-document.ts (1 hunks)
  • apps/web/utils/drive/client.ts (1 hunks)
  • apps/web/utils/drive/document-extraction.ts (1 hunks)
  • apps/web/utils/drive/oauth-callback-helpers.ts (1 hunks)
  • apps/web/utils/filebot/is-filebot-email.test.ts (1 hunks)
  • apps/web/utils/filebot/is-filebot-email.ts (1 hunks)
  • apps/web/utils/redis/oauth-code.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/web/utils/filebot/is-filebot-email.test.ts
  • apps/web/app/(app)/[emailAccountId]/drive/DriveConnectionCard.tsx
🧰 Additional context used
📓 Path-based instructions (14)
**/*.{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/utils/redis/oauth-code.ts
  • apps/web/utils/ai/document-filing/analyze-document.ts
  • apps/web/utils/drive/client.ts
  • apps/web/utils/filebot/is-filebot-email.ts
  • apps/web/utils/drive/document-extraction.ts
  • apps/web/utils/drive/oauth-callback-helpers.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/utils/redis/oauth-code.ts
  • apps/web/utils/ai/document-filing/analyze-document.ts
  • apps/web/utils/drive/client.ts
  • apps/web/utils/filebot/is-filebot-email.ts
  • apps/web/utils/drive/document-extraction.ts
  • apps/web/utils/drive/oauth-callback-helpers.ts
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

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

apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Use @/ path aliases for imports from project root
Follow tailwindcss patterns with prettier-plugin-tailwindcss class organization
Prefix client-side environment variables with NEXT_PUBLIC_
Leverage TypeScript inference for better developer experience with type exports from API routes

Files:

  • apps/web/utils/redis/oauth-code.ts
  • apps/web/utils/ai/document-filing/analyze-document.ts
  • apps/web/utils/drive/client.ts
  • apps/web/utils/filebot/is-filebot-email.ts
  • apps/web/utils/drive/document-extraction.ts
  • apps/web/utils/drive/oauth-callback-helpers.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/utils/redis/oauth-code.ts
  • apps/web/utils/ai/document-filing/analyze-document.ts
  • apps/web/utils/drive/client.ts
  • apps/web/utils/filebot/is-filebot-email.ts
  • apps/web/utils/drive/document-extraction.ts
  • apps/web/utils/drive/oauth-callback-helpers.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/utils/redis/oauth-code.ts
  • apps/web/utils/ai/document-filing/analyze-document.ts
  • apps/web/utils/drive/client.ts
  • apps/web/utils/filebot/is-filebot-email.ts
  • apps/web/utils/drive/document-extraction.ts
  • apps/web/utils/drive/oauth-callback-helpers.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/utils/redis/oauth-code.ts
  • apps/web/utils/ai/document-filing/analyze-document.ts
  • apps/web/utils/drive/client.ts
  • apps/web/utils/filebot/is-filebot-email.ts
  • apps/web/utils/drive/document-extraction.ts
  • apps/web/utils/drive/oauth-callback-helpers.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/utils/redis/oauth-code.ts
  • apps/web/utils/ai/document-filing/analyze-document.ts
  • apps/web/utils/drive/client.ts
  • apps/web/utils/filebot/is-filebot-email.ts
  • apps/web/utils/drive/document-extraction.ts
  • apps/web/utils/drive/oauth-callback-helpers.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/utils/redis/oauth-code.ts
  • apps/web/utils/ai/document-filing/analyze-document.ts
  • apps/web/utils/drive/client.ts
  • apps/web/utils/filebot/is-filebot-email.ts
  • apps/web/utils/drive/document-extraction.ts
  • apps/web/utils/drive/oauth-callback-helpers.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/utils/redis/oauth-code.ts
  • apps/web/utils/ai/document-filing/analyze-document.ts
  • apps/web/utils/drive/client.ts
  • apps/web/utils/filebot/is-filebot-email.ts
  • apps/web/utils/drive/document-extraction.ts
  • apps/web/utils/drive/oauth-callback-helpers.ts
apps/web/**/*.{ts,tsx,js,jsx}

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

apps/web/**/*.{ts,tsx,js,jsx}: Use proper error handling with try/catch blocks
Prefer self-documenting code over comments; use descriptive variable and function names instead of explaining intent with comments
Add helper functions to the bottom of files, not the top

Files:

  • apps/web/utils/redis/oauth-code.ts
  • apps/web/utils/ai/document-filing/analyze-document.ts
  • apps/web/utils/drive/client.ts
  • apps/web/utils/filebot/is-filebot-email.ts
  • apps/web/utils/drive/document-extraction.ts
  • apps/web/utils/drive/oauth-callback-helpers.ts
apps/web/**/*.{ts,tsx,js,jsx,json,css,md}

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

Format code with Prettier

Files:

  • apps/web/utils/redis/oauth-code.ts
  • apps/web/utils/ai/document-filing/analyze-document.ts
  • apps/web/utils/drive/client.ts
  • apps/web/utils/filebot/is-filebot-email.ts
  • apps/web/utils/drive/document-extraction.ts
  • apps/web/utils/drive/oauth-callback-helpers.ts
**/{utils,helpers,lib}/**/*.{ts,tsx}

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

Logger should be passed as a parameter to helper functions instead of creating their own logger instances

Files:

  • apps/web/utils/redis/oauth-code.ts
  • apps/web/utils/ai/document-filing/analyze-document.ts
  • apps/web/utils/drive/client.ts
  • apps/web/utils/filebot/is-filebot-email.ts
  • apps/web/utils/drive/document-extraction.ts
  • apps/web/utils/drive/oauth-callback-helpers.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/utils/ai/document-filing/analyze-document.ts
apps/web/utils/ai/**/*.ts

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

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
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
System prompts must define the LLM's role and task specifications
User prompts must contain the actual data and context, and should be kept separate from system prompts
Always define a Zod schema for LLM response validation and make schemas as specific as possible to guide the LLM output
Use descriptive scoped loggers for each LLM feature, log inputs and outputs with appropriate log levels, and include relevant context in log messages
Implement early returns for invalid LLM inputs, use proper error types and logging, implement fallbacks for AI failures, and add retry logic for transient failures using withRetry
Use XML-like tags to structure data in prompts, remove excessive whitespace and truncate long inputs, and format data consistently across similar LLM functions
Use TypeScript types for all LLM function parameters and return values, and define clear interfaces for complex input/output structures
Keep related AI functions in the same file or directory, extract common patterns into utility functions, and document complex AI logic with clear comments

Files:

  • apps/web/utils/ai/document-filing/analyze-document.ts
🧠 Learnings (36)
📚 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/utils/ai/document-filing/analyze-document.ts
  • apps/web/utils/filebot/is-filebot-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 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/utils/ai/document-filing/analyze-document.ts
  • apps/web/utils/filebot/is-filebot-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 : Keep related AI functions in the same file or directory, extract common patterns into utility functions, and document complex AI logic with clear comments

Applied to files:

  • apps/web/utils/ai/document-filing/analyze-document.ts
  • apps/web/utils/filebot/is-filebot-email.ts
  • apps/web/utils/drive/document-extraction.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 TypeScript types for all LLM function parameters and return values, and define clear interfaces for complex input/output structures

Applied to files:

  • apps/web/utils/ai/document-filing/analyze-document.ts
  • apps/web/utils/drive/document-extraction.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,utils/llms,__tests__}/**/*.ts : 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

Applied to files:

  • apps/web/utils/ai/document-filing/analyze-document.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/ai/document-filing/analyze-document.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 : Always define a Zod schema for LLM response validation and make schemas as specific as possible to guide the LLM output

Applied to files:

  • apps/web/utils/ai/document-filing/analyze-document.ts
📚 Learning: 2025-12-17T11:18:06.830Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-17T11:18:06.830Z
Learning: Applies to apps/web/**/utils/actions/*.validation.ts : Create separate Zod validation schema files for server action inputs

Applied to files:

  • apps/web/utils/ai/document-filing/analyze-document.ts
📚 Learning: 2025-11-25T14:39:49.448Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-11-25T14:39:49.448Z
Learning: Applies to apps/web/utils/actions/*.validation.ts : Create separate validation files for server actions using the naming convention `apps/web/utils/actions/NAME.validation.ts` containing Zod schemas and inferred types

Applied to files:

  • apps/web/utils/ai/document-filing/analyze-document.ts
📚 Learning: 2025-11-25T14:39:49.448Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-11-25T14:39:49.448Z
Learning: Applies to apps/web/utils/actions/*.ts : Use `.schema()` method with Zod validation schemas from corresponding `.validation.ts` files in next-safe-action configuration

Applied to files:

  • apps/web/utils/ai/document-filing/analyze-document.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 : Implement early returns for invalid LLM inputs, use proper error types and logging, implement fallbacks for AI failures, and add retry logic for transient failures using `withRetry`

Applied to files:

  • apps/web/utils/ai/document-filing/analyze-document.ts
  • apps/web/utils/filebot/is-filebot-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 : System prompts must define the LLM's role and task specifications

Applied to files:

  • apps/web/utils/ai/document-filing/analyze-document.ts
📚 Learning: 2025-11-25T14:37:09.306Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-11-25T14:37:09.306Z
Learning: Applies to apps/web/utils/actions/*.ts : Use `next-safe-action` with Zod schemas for all server actions (create/update/delete mutations), storing validation schemas in `apps/web/utils/actions/*.validation.ts`

Applied to files:

  • apps/web/utils/ai/document-filing/analyze-document.ts
  • apps/web/utils/drive/oauth-callback-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 : All input parameters must be validated - check for presence, type, and format before use; use Zod schemas to validate request bodies with type guards and constraints

Applied to files:

  • apps/web/utils/ai/document-filing/analyze-document.ts
📚 Learning: 2025-11-25T14:36:51.389Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/form-handling.mdc:0-0
Timestamp: 2025-11-25T14:36:51.389Z
Learning: Applies to **/*.validation.ts : Use descriptive error messages in Zod validation schemas

Applied to files:

  • apps/web/utils/ai/document-filing/analyze-document.ts
📚 Learning: 2025-11-25T14:36:51.389Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/form-handling.mdc:0-0
Timestamp: 2025-11-25T14:36:51.389Z
Learning: Applies to **/*.validation.ts : Define validation schemas using Zod

Applied to files:

  • apps/web/utils/ai/document-filing/analyze-document.ts
📚 Learning: 2025-11-25T14:37:09.306Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-11-25T14:37:09.306Z
Learning: Applies to apps/web/utils/actions/*.validation.ts : Define Zod validation schemas in separate `*.validation.ts` files and export both the schema and inferred type (e.g., `CreateExampleBody`)

Applied to files:

  • apps/web/utils/ai/document-filing/analyze-document.ts
📚 Learning: 2025-11-25T14:36:53.147Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/form-handling.mdc:0-0
Timestamp: 2025-11-25T14:36:53.147Z
Learning: Applies to **/*.validation.{ts,tsx} : Define validation schemas using Zod

Applied to files:

  • apps/web/utils/ai/document-filing/analyze-document.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/ai/document-filing/analyze-document.ts
  • apps/web/utils/drive/document-extraction.ts
📚 Learning: 2025-12-17T11:18:06.830Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-17T11:18:06.830Z
Learning: Applies to apps/web/**/{app/api,utils/actions}/**/*.ts : Use `withAuth` for user-level operations and `withEmailAccount` for email-account-level operations

Applied to files:

  • apps/web/utils/drive/client.ts
  • apps/web/utils/filebot/is-filebot-email.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/utils/drive/client.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 : Maintain consistent error response format across all API routes to avoid information disclosure while providing meaningful error feedback

Applied to files:

  • apps/web/utils/drive/client.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 `SafeError` for error responses to prevent information disclosure - provide generic messages (e.g., 'Rule not found' not 'Rule {id} does not exist for user {userId}') without revealing internal IDs or ownership details

Applied to files:

  • apps/web/utils/drive/client.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/utils/filebot/is-filebot-email.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/utils/filebot/is-filebot-email.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 : 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/utils/filebot/is-filebot-email.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/utils/filebot/is-filebot-email.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/utils/filebot/is-filebot-email.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/utils/filebot/is-filebot-email.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/utils/filebot/is-filebot-email.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 **/*.ts : Always validate that resources belong to the authenticated user before any operation - use ownership checks in queries (e.g., `emailAccount: { id: emailAccountId }`) and throw `SafeError` if validation fails

Applied to files:

  • apps/web/utils/filebot/is-filebot-email.ts
  • apps/web/utils/drive/oauth-callback-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/utils/filebot/is-filebot-email.ts
  • apps/web/utils/drive/oauth-callback-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 : Prevent mass assignment vulnerabilities by explicitly whitelisting allowed fields in updates - never directly assign request body to database update, always validate with schemas and only update specific fields

Applied to files:

  • apps/web/utils/drive/oauth-callback-helpers.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 : Use Prisma relationships for access control by leveraging nested where clauses (e.g., `emailAccount: { id: emailAccountId }`) to validate ownership

Applied to files:

  • apps/web/utils/drive/oauth-callback-helpers.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 : Prevent Insecure Direct Object References (IDOR) by validating resource ownership before operations. All `findUnique`/`findFirst` calls MUST include ownership filters

Applied to files:

  • apps/web/utils/drive/oauth-callback-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 **/*.ts : ALL database queries MUST be scoped to the authenticated user/account - include user/account filtering in `where` clauses (e.g., `emailAccountId`, `userId`) to ensure users only access their own resources

Applied to files:

  • apps/web/utils/drive/oauth-callback-helpers.ts
🧬 Code graph analysis (4)
apps/web/utils/ai/document-filing/analyze-document.ts (4)
apps/web/utils/drive/types.ts (1)
  • DriveFolder (5-11)
apps/web/utils/llms/types.ts (1)
  • EmailAccountWithAI (10-32)
apps/web/utils/llms/index.ts (1)
  • createGenerateObject (139-217)
apps/web/utils/drive/document-extraction.ts (1)
  • cleanExtractedText (233-239)
apps/web/utils/drive/client.ts (2)
apps/web/env.ts (1)
  • env (17-258)
apps/web/utils/drive/scopes.ts (2)
  • GOOGLE_DRIVE_SCOPES (18-24)
  • MICROSOFT_DRIVE_SCOPES (4-13)
apps/web/utils/filebot/is-filebot-email.ts (2)
apps/web/env.ts (1)
  • env (17-258)
apps/web/utils/email.ts (1)
  • extractEmailAddress (19-52)
apps/web/utils/drive/oauth-callback-helpers.ts (6)
apps/web/utils/calendar/oauth-callback-helpers.ts (1)
  • validateOAuthCallback (26-56)
apps/web/utils/logger.ts (1)
  • Logger (5-5)
apps/web/utils/drive/constants.ts (1)
  • DRIVE_STATE_COOKIE_NAME (1-1)
apps/web/env.ts (1)
  • env (17-258)
apps/web/utils/oauth/redirect.ts (1)
  • RedirectError (7-17)
apps/web/utils/oauth/state.ts (1)
  • parseOAuthState (25-29)
🪛 ast-grep (0.40.0)
apps/web/utils/filebot/is-filebot-email.ts

[warning] 26-28: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(
^${escapeRegex(localPart)}\\+${FILEBOT_PREFIX}-[a-zA-Z0-9]+@${escapeRegex(domain)}$,
)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 69-71: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(
^${escapeRegex(localPart)}\\+${FILEBOT_PREFIX}-([a-zA-Z0-9]+)@${escapeRegex(domain)}$,
)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)

⏰ 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 (19)
apps/web/utils/filebot/is-filebot-email.ts (6)

1-6: LGTM!

The environment-dependent prefix pattern is well-designed, allowing clean separation between production and development filebot emails.


21-25: Validation implemented correctly.

The validation for localPart and domain after splitting, as well as the check for extractedEmailToCheck, properly addresses the concerns raised in previous reviews. The function now safely handles malformed email addresses.


27-30: ReDoS warning is likely a false positive.

The static analysis tool flags potential ReDoS vulnerability, but the implementation is safe because:

  • User-controlled parts (localPart and domain) are escaped via escapeRegex
  • Pattern is anchored with ^ and $
  • Token pattern [a-zA-Z0-9]+ is simple and linear
  • No nested quantifiers or catastrophic backtracking patterns

The current implementation properly mitigates ReDoS risks.


64-68: Validation implemented correctly.

The validation checks for localPart, domain, and extractedEmailToCheck properly handle edge cases and address concerns from previous reviews.


70-74: ReDoS concern addressed by proper escaping.

Similar to isFilebotEmail, the static analysis warning is a false positive. The escapeRegex function properly sanitizes user input, and the pattern structure prevents ReDoS attacks. The capture group correctly extracts the token portion.


77-79: LGTM!

The escapeRegex helper correctly escapes all regex special characters, ensuring that user-controlled email parts cannot introduce regex injection or ReDoS vulnerabilities.

apps/web/utils/drive/document-extraction.ts (6)

37-41: Past issue resolved: .doc MIME type removed.

The previous reviews flagged that application/msword was listed but had no handler. This has been correctly addressed by removing it from EXTRACTABLE_MIME_TYPES.


93-104: LGTM: Native PDF support detection is well-designed.

The function correctly checks Claude's documented limits (32MB, 100 pages) and provides a clear API for determining when native PDF support can be used instead of extraction.


110-168: LGTM: PDF extraction logic is correct.

The implementation correctly:

  • Processes pages sequentially with configurable limits
  • Tracks both character length and page count limits
  • Handles truncation properly
  • Logs useful debugging information

The type assertion on Line 134 is acceptable given unpdf's loose typing.


174-198: LGTM: DOCX extraction is straightforward and correct.

The implementation properly uses mammoth's extractRawText API and handles truncation. Logging the mammoth messages is helpful for debugging.


233-239: LGTM: Text cleaning aligns with LLM best practices.

The function correctly normalizes whitespace for LLM consumption, following the patterns from retrieved learnings about removing excessive whitespace.

Based on learnings about formatting data consistently for LLM functions.


180-182: Remove unnecessary @ts-expect-error suppression — mammoth v1.11.0 includes built-in TypeScript declarations.

Mammoth added TypeScript declarations in version 1.4.19, and the project has mammoth 1.11.0 already installed. Since the package includes types out-of-the-box, the @ts-expect-error comment is unnecessary. The comment suggesting to "pnpm add mammoth" is also misleading—the package is already a dependency. Simply import mammoth directly; TypeScript should resolve types without suppression.

Likely an incorrect or invalid review comment.

apps/web/utils/drive/client.ts (4)

12-31: LGTM: Google OAuth configuration is correct.

The implementation properly includes prompt: "consent" and access_type: "offline" to ensure refresh tokens are issued.


36-65: LGTM: Google token exchange follows security best practices.

The implementation correctly:

  • Validates all required tokens are present
  • Uses official Google ID token verification
  • Validates audience claim
  • Extracts user email from verified token

117-122: Past issue resolved: Error handling now checks response status first.

The previous review flagged calling response.json() before checking response.ok. This has been correctly fixed - the code now checks response.ok first and handles error bodies safely.


94-154: LGTM: Microsoft token exchange is well-implemented.

The implementation correctly:

  • Validates environment configuration
  • Handles error responses safely
  • Validates required tokens are present
  • Fetches user profile to obtain email
  • Provides email fallback (mail || userPrincipalName)
  • Calculates token expiration time
apps/web/utils/drive/oauth-callback-helpers.ts (3)

32-36: LGTM: State validation schema is well-defined.

The Zod schema properly validates the OAuth state with appropriate length constraints to prevent abuse.


76-101: LGTM: State parsing and validation is robust.

The implementation correctly:

  • Handles parse errors with try-catch
  • Uses Zod for type-safe validation
  • Logs detailed error information
  • Throws appropriate redirect errors

147-171: Past issue resolved: Now uses atomic upsert.

The previous reviews flagged a race condition with the findFirst + create/update pattern. This has been correctly fixed by using Prisma's upsert with the compound unique constraint emailAccountId_provider, which provides atomic database-level guarantees.

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.

3 issues found across 15 files (changes from recent commits).

Prompt for AI agents (all 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]/drive/FilingPreferences.tsx">

<violation number="1" location="apps/web/app/(app)/[emailAccountId]/drive/FilingPreferences.tsx:305">
P1: Double event trigger: clicking the Checkbox fires both its `onCheckedChange` and the parent div&#39;s `onClick`, causing `handleFolderToggle` to be called twice and triggering duplicate API calls. Add `onClick={(e) =&gt; e.stopPropagation()}` to the Checkbox to prevent event bubbling.</violation>
</file>

<file name="apps/web/app/api/user/unsubscribe-one-click/route.ts">

<violation number="1" location="apps/web/app/api/user/unsubscribe-one-click/route.ts:69">
P1: Potential SSRF vulnerability: The `fetch` request is made to a URL extracted from email headers, which could be attacker-controlled. Consider validating that the URL is a public HTTP/HTTPS URL and not pointing to internal/private IP ranges (localhost, 10.x.x.x, 172.16.x.x, 192.168.x.x, 169.254.x.x, etc.) before making the request.</violation>
</file>

<file name="apps/web/utils/drive/handle-filing-reply.test.ts">

<violation number="1" location="apps/web/utils/drive/handle-filing-reply.test.ts:9">
P1: These tests define local copies of `parseFolderPath` and `isSkipCommand` rather than testing the actual production functions from `handle-filing-reply.ts`. This is a testing anti-pattern - if the production code changes, these tests won&#39;t detect regressions since they&#39;re testing their own inline implementations.

Consider either:
1. Export these functions (possibly via a separate `@internal` export) for testing
2. Test them indirectly through the public `handleFilingReply` function
3. Use a technique like `rewire` or test the module exports directly</violation>
</file>

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

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: 6

♻️ Duplicate comments (1)
apps/web/prisma/schema.prisma (1)

1002-1038: Make folderId and folderPath nullable for PENDING status.

As noted in the previous review, these fields are required (non-nullable) but documents with PENDING status (line 1167) will not have a determined destination folder yet. This will cause errors when creating DocumentFiling records before the AI determines where to file the document.

🔎 Proposed fix
   // Result (after filing)
-  folderId   String
-  folderPath String
+  folderId   String?
+  folderPath String?
   fileId     String?
   reasoning  String?
   confidence Float?

After updating the schema, run migration and regenerate the Prisma client:

#!/bin/bash
npx prisma migrate dev --name make_filing_folders_optional
npx prisma generate

Then verify all TypeScript code that accesses these fields handles null values appropriately.

🧹 Nitpick comments (10)
apps/web/utils/drive/handle-filing-reply.test.ts (1)

3-27: Consider exporting helper functions for proper test coverage.

The test file duplicates the implementation of parseFolderPath and isSkipCommand inline rather than testing the actual functions from handle-filing-reply.ts. This means if the real implementation changes, these tests won't catch regressions.

Consider either:

  1. Exporting the helper functions from handle-filing-reply.ts for direct testing
  2. Creating an internal handle-filing-reply.internal.ts module that exports these helpers
  3. Testing through the public processFilingReply function with proper mocking
apps/web/utils/drive/handle-filing-reply.ts (4)

126-138: Use Prisma enum instead of string literal for status.

Per coding guidelines, import Prisma enums from @/generated/prisma/enums instead of using string literals. This applies to "FILED" on line 131.

🔎 Proposed fix

Add import at the top:

import { DocumentFilingStatus } from "@/generated/prisma/enums";

Then update the status assignment:

       data: {
         folderId: targetFolder.id,
         folderPath: folderPath,
-        status: "FILED",
+        status: DocumentFilingStatus.FILED,
         wasCorrected: filing.status === "FILED",

157-164: Use Prisma enum for ERROR status as well.

🔎 Proposed fix
     await prisma.documentFiling.update({
       where: { id: filing.id },
-      data: { status: "ERROR" },
+      data: { status: DocumentFilingStatus.ERROR },
     });

121-145: TODO: File moving and upload functionality is incomplete.

The code has TODO comments for:

  1. Implementing file moving for corrections (Line 123)
  2. Fetching attachment and uploading to specified folder for pending filings (Line 143)

These represent incomplete functionality where the filing record is updated but the actual file operations don't occur.

Would you like me to help implement the file moving logic or open issues to track these tasks?


218-224: Use Prisma enum for REJECTED status.

🔎 Proposed fix
 async function handleSkip(filingId: string, logger: Logger): Promise<void> {
   await prisma.documentFiling.update({
     where: { id: filingId },
-    data: { status: "REJECTED" },
+    data: { status: DocumentFilingStatus.REJECTED },
   });
   logger.info("Filing skipped by user");
 }
apps/web/app/api/user/drive/folders/route.ts (1)

57-78: Consider parallel folder fetching for better performance.

The sequential for...of loop fetches folders from each drive one at a time. With multiple connected drives, this could be slow.

🔎 Proposed optimization using Promise.all
-  for (const connection of driveConnections) {
-    try {
-      const provider = createDriveProvider(connection, logger);
-      const folders = await provider.listFolders(undefined);
-
-      for (const folder of folders) {
-        availableFolders.push({
-          id: folder.id,
-          name: folder.name,
-          path: folder.name,
-          driveConnectionId: connection.id,
-          provider: connection.provider,
-        });
-      }
-    } catch (error) {
-      logger.warn("Error fetching folders from drive", {
-        connectionId: connection.id,
-        provider: connection.provider,
-        error,
-      });
-    }
-  }
+  const folderResults = await Promise.allSettled(
+    driveConnections.map(async (connection) => {
+      const provider = createDriveProvider(connection, logger);
+      const folders = await provider.listFolders(undefined);
+      return folders.map((folder) => ({
+        id: folder.id,
+        name: folder.name,
+        path: folder.name,
+        driveConnectionId: connection.id,
+        provider: connection.provider,
+      }));
+    }),
+  );
+
+  for (const [index, result] of folderResults.entries()) {
+    if (result.status === "fulfilled") {
+      availableFolders.push(...result.value);
+    } else {
+      const connection = driveConnections[index];
+      logger.warn("Error fetching folders from drive", {
+        connectionId: connection.id,
+        provider: connection.provider,
+        error: result.reason,
+      });
+    }
+  }
apps/web/utils/actions/drive.ts (1)

101-112: Consider including ownership filter in the query.

The current pattern queries by id then validates ownership separately. While functionally safe, the project guidelines prefer including ownership filters directly in WHERE clauses.

🔎 Proposed improvement
 export const removeFilingFolderAction = actionClient
   .metadata({ name: "removeFilingFolder" })
   .inputSchema(removeFilingFolderBody)
   .action(async ({ ctx: { emailAccountId }, parsedInput: { id } }) => {
     const folder = await prisma.filingFolder.findUnique({
-      where: { id },
+      where: { id, emailAccountId },
     });

-    if (!folder || folder.emailAccountId !== emailAccountId) {
+    if (!folder) {
       throw new SafeError("Filing folder not found");
     }

     await prisma.filingFolder.delete({
       where: { id },
     });
   });
apps/web/app/api/user/unsubscribe-one-click/route.ts (1)

27-28: Consider wrapping Zod parse in try/catch for better error responses.

If the request body fails Zod validation, .parse() throws a ZodError which may not be handled gracefully by the middleware, potentially exposing validation details.

🔎 Proposed safer parsing
     const body = await request.json();
-    const { senderEmail } = unsubscribeSchema.parse(body);
+    const parsed = unsubscribeSchema.safeParse(body);
+    if (!parsed.success) {
+      return NextResponse.json(
+        { success: false, method: "fallback", error: "Invalid request" } satisfies OneClickUnsubscribeResponse,
+        { status: 400 },
+      );
+    }
+    const { senderEmail } = parsed.data;
apps/web/utils/drive/filing-engine.ts (1)

183-184: Consider extracting confidence threshold as a constant.

The 0.7 threshold is a magic number that determines whether to ask the user or file automatically. Consider extracting this to a named constant for clarity and easier adjustment.

🔎 Proposed improvement
+const CONFIDENCE_THRESHOLD = 0.7;
+
 // Step 7: Determine if we should ask the user first
-const shouldAsk = analysis.confidence < 0.7;
+const shouldAsk = analysis.confidence < CONFIDENCE_THRESHOLD;
apps/web/app/(app)/[emailAccountId]/drive/FilingPreferences.tsx (1)

179-193: Consider wrapping handleFolderToggle in useCallback.

While not critical for correctness, wrapping this handler in useCallback would follow React best practices and prevent unnecessary re-creation on every render.

🔎 Proposed refactor
+  const handleFolderToggle = useCallback((folder: FolderItem, isChecked: boolean) => {
-  const handleFolderToggle = (folder: FolderItem, isChecked: boolean) => {
     if (isChecked) {
       addFolder({
         folderId: folder.id,
         folderName: folder.name,
         folderPath: folder.path,
         driveConnectionId: folder.driveConnectionId,
       });
     } else {
       const saved = savedFolders.find((f) => f.folderId === folder.id);
       if (saved) {
         removeFolder({ id: saved.id });
       }
     }
-  };
+  }, [addFolder, removeFolder, savedFolders]);

Comment on lines +765 to +774
if (result?.serverError) {
toastError({
title: "Error saving rules",
description: result.serverError,
});
} else {
mutateEmail();
// Trigger preview after successful save
onPreviewClick();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

onSubmit ignores result?.validationErrors, so preview proceeds after a failed server-side validation. Consider handling validationErrors and returning early, similar to serverError.

Suggested change
if (result?.serverError) {
toastError({
title: "Error saving rules",
description: result.serverError,
});
} else {
mutateEmail();
// Trigger preview after successful save
onPreviewClick();
}
if (result?.serverError || result?.validationErrors) {
toastError({
title: "Error saving rules",
description: result.serverError || "Validation failed. Please check your input.",
});
return;
} else {
mutateEmail();
// Trigger preview after successful save
onPreviewClick();
}

🚀 Want me to fix this? Reply ex: "fix it for me".

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 1 file (changes from recent commits).

Prompt for AI agents (all 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]/drive/DriveSetup.tsx">

<violation number="1" location="apps/web/app/(app)/[emailAccountId]/drive/DriveSetup.tsx:110">
P2: Missing `.catch()` handler on the promise chain. If `fileAttachmentAction` throws an exception (network error, etc.), it will result in an unhandled promise rejection and the filing state will remain stuck at &quot;filing&quot; status.</violation>
</file>

Reply to cubic to teach it or ask questions. Tag @cubic-dev-ai to re-run a review.

fileAttachmentAction(emailAccountId, {
messageId: att.messageId,
filename: att.filename,
}).then((result) => {
Copy link
Contributor

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

Choose a reason for hiding this comment

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

P2: Missing .catch() handler on the promise chain. If fileAttachmentAction throws an exception (network error, etc.), it will result in an unhandled promise rejection and the filing state will remain stuck at "filing" status.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/app/(app)/[emailAccountId]/drive/DriveSetup.tsx, line 110:

<comment>Missing `.catch()` handler on the promise chain. If `fileAttachmentAction` throws an exception (network error, etc.), it will result in an unhandled promise rejection and the filing state will remain stuck at &quot;filing&quot; status.</comment>

<file context>
@@ -82,11 +89,54 @@ export function DriveSetup() {
+          fileAttachmentAction(emailAccountId, {
+            messageId: att.messageId,
+            filename: att.filename,
+          }).then((result) =&gt; {
+            const resultData = result?.data;
+            if (result?.serverError) {
</file context>
Fix with Cubic

userPhase === "previewing" || userPhase === "starting";
const { data: attachmentsData, isLoading: attachmentsLoading } =
useFilingPreviewAttachments(shouldFetchAttachments, {
onSuccess: (data) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Race condition: setFilingStates(initial) runs after starting fileAttachmentAction promises and can overwrite resolved updates. Consider setting the initial state before firing the promises.

🚀 Want me to fix this? Reply ex: "fix it for me".

Comment on lines +55 to +62
.filter("folder ne null") // Only get folders, not files
.select("id,name,parentReference,webUrl")
.top(200) // Increase limit for better visibility
.get();

const items: DriveItem[] = response.value || [];

return items.map((item) => this.convertToFolder(item));
Copy link
Contributor

Choose a reason for hiding this comment

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

$filter=folder ne null likely doesn’t work on Graph DriveItem facets and may return files. Consider removing the server-side filter and filter client-side with item.folder before mapping so only folders are returned.

-         .filter("folder ne null") // Only get folders, not files
-       return items.map((item) => this.convertToFolder(item));
+       return items.filter((item) => item.folder).map((item) => this.convertToFolder(item));

🚀 Want me to fix this? Reply ex: "fix it for me".

const replyToAddress = getFilebotEmail({ userEmail });

const subject = `✓ Filed ${filing.filename}`;
const messageHtml = buildFiledEmailHtml({
Copy link
Contributor

Choose a reason for hiding this comment

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

filing.driveConnection.provider may throw if driveConnection is null. Consider guarding for a missing relation and log/return before building the email.

Suggested change
const messageHtml = buildFiledEmailHtml({
if (!filing.driveConnection) {
log.error("Filing missing driveConnection");
return;
}
const messageHtml = buildFiledEmailHtml({

🚀 Want me to fix this? Reply ex: "fix it for me".

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 9 files (changes from recent commits).

Prompt for AI agents (all 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]/drive/FilingActivity.tsx">

<violation number="1" location="apps/web/app/(app)/[emailAccountId]/drive/FilingActivity.tsx:83">
P2: Unsafe type assertion: `connection?.provider` can be `undefined` when no connection is found, but `as DriveProviderType` assertion silences TypeScript without handling the undefined case. Consider keeping a runtime fallback or adding a guard.</violation>
</file>

Reply to cubic to teach it or ask questions. Tag @cubic-dev-ai to re-run a review.

const connection = connections.find((c) => c.id === filing.driveConnectionId);

const driveUrl = filing.fileId
? getDriveFileUrl(filing.fileId, connection?.provider as DriveProviderType)
Copy link
Contributor

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

Choose a reason for hiding this comment

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

P2: Unsafe type assertion: connection?.provider can be undefined when no connection is found, but as DriveProviderType assertion silences TypeScript without handling the undefined case. Consider keeping a runtime fallback or adding a guard.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/app/(app)/[emailAccountId]/drive/FilingActivity.tsx, line 83:

<comment>Unsafe type assertion: `connection?.provider` can be `undefined` when no connection is found, but `as DriveProviderType` assertion silences TypeScript without handling the undefined case. Consider keeping a runtime fallback or adding a guard.</comment>

<file context>
@@ -79,7 +80,7 @@ function FilingRow({
 
   const driveUrl = filing.fileId
-    ? getDriveFileUrl(filing.fileId, connection?.provider || &quot;&quot;)
+    ? getDriveFileUrl(filing.fileId, connection?.provider as DriveProviderType)
     : null;
 
</file context>
Fix with Cubic

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: 2

♻️ Duplicate comments (18)
apps/web/components/kibo-ui/tree/index.tsx (3)

190-190: Unused onClick prop.

The onClick prop is destructured but never used. Per past review comments, either pass it to the <div> at line 217 or remove it from destructuring to let it flow via ...props.


306-306: Width calculation can produce negative values.

Per past review comments, when indent is less than 4, the width: (indent ?? 0) - 4 calculation produces a negative value, which may render oddly. Consider clamping to 0.

🔎 Suggested fix
-          width: (indent ?? 0) - 4,
+          width: Math.max(0, (indent ?? 0) - 4),

86-95: Semi-controlled state can cause stale UI.

This issue was previously flagged: when selectedIds is provided without onSelectionChange, internalSelectedIds is initialized once but never syncs with subsequent selectedIds prop updates, leading to stale selection state.

Based on past review comments, consider either:

  1. Treating selectedIds as controlled whenever provided (read-only mode is valid)
  2. Adding a useEffect to sync internalSelectedIds when selectedIds changes
apps/web/app/(app)/[emailAccountId]/drive/FilingActivity.tsx (1)

82-84: Guard provider before calling getDriveFileUrl to prevent runtime errors.

The type assertion as DriveProviderType at line 83 is unsafe. When connection is undefined (e.g., if the matching connection hasn't loaded yet or doesn't exist), connection?.provider evaluates to undefined, but the type assertion tricks TypeScript into thinking it's a valid DriveProviderType. This causes getDriveFileUrl to hit its default case, log an exception for "Invalid provider", and return an invalid value.

🔎 Recommended fix
-  const driveUrl = filing.fileId
-    ? getDriveFileUrl(filing.fileId, connection?.provider as DriveProviderType)
+  const driveUrl = filing.fileId && connection?.provider
+    ? getDriveFileUrl(filing.fileId, connection.provider)
     : null;

Based on past review comments from cubic-dev-ai[bot] and macroscopeapp[bot].

apps/web/app/(app)/[emailAccountId]/drive/AllowedFolders.tsx (5)

263-263: TreeExpander hasChildren is hardcoded.

Hardcoding hasChildren={true} always shows the expand chevron, even for folders that truly have no children. This is a duplicate of an earlier review comment.


106-106: Action calls still use incorrect signature.

Both addFilingFolderAction and removeFilingFolderAction are called with emailAccountId as the first parameter, but actionClient automatically provides emailAccountId via context. Per the established pattern, these should be called with only the input object.

Based on learnings, actionClient provides emailAccountId via ctx automatically.

Also applies to: 122-122


289-292: Missing driveConnectionId inheritance in recursive calls.

When constructing child folder objects for recursion, subfolder.driveConnectionId may be undefined. The child FolderNode then calls useDriveSubfolders with undefined, causing the hook to fail. Consider inheriting from the parent as suggested in the earlier review.


299-299: knownChildren not threaded through recursion.

Recursive FolderNode calls at line 287 don't receive knownChildren, so deeper levels always refetch from the server instead of using cached data from folderChildrenMap. This impacts performance with unnecessary requests. This is a duplicate of an earlier comment.


377-377: Dialog onOpenChange handler ignores boolean parameter.

onOpenChange receives a boolean (open) but onToggle doesn't accept it, so closing via ESC or backdrop click will toggle the dialog back open. This is a duplicate of an earlier review comment.

apps/web/utils/drive/document-extraction.ts (2)

128-153: Truncation logic doesn't account for separators (known issue).

The current implementation tracks totalLength based on individual page lengths but doesn't account for the "\n\n" separators added during join() at line 153. This can cause the final text to exceed maxLength by up to (pageCount - 1) * 2 characters.

As noted in previous reviews, consider incorporating separator length into the truncation check:

Suggested adjustment
     for (let i = 1; i <= pagesToProcess && !truncated; i++) {
       const page = await pdf.getPage(i);
       const textContent = await page.getTextContent();
 
       const pageText = (textContent.items as Array<{ str?: string }>)
         .map((item) => item.str ?? "")
         .join(" ");
 
+      const sepLen = textParts.length > 0 ? 2 : 0;
-      if (totalLength + pageText.length > maxLength) {
+      if (totalLength + sepLen + pageText.length > maxLength) {
-        const remaining = maxLength - totalLength;
+        const remaining = maxLength - totalLength - sepLen;
-        textParts.push(pageText.slice(0, remaining));
+        if (remaining > 0) textParts.push(pageText.slice(0, remaining));
         truncated = true;
       } else {
         textParts.push(pageText);
-        totalLength += pageText.length;
+        totalLength += sepLen + pageText.length;
       }
     }

177-199: DOCX extraction improved but memory concern remains.

Good improvement: logging now uses messageCount instead of exposing potentially sensitive messages content.

The full-text extraction before truncation (line 185) is still a potential memory concern for very large DOCX files, as noted in previous reviews. This appears to be a limitation of mammoth's API, which doesn't offer streaming. Consider documenting this limitation in a comment or adding a buffer-size pre-check if memory issues arise in production.

apps/web/utils/drive/handle-filing-reply.ts (4)

201-222: Improve folder path parsing robustness.

Two issues limit the parser's effectiveness:

  1. Line 208: The filter !trimmed.startsWith("On ") is too broad and would skip valid folder paths like "On Hold/2024" or "Ongoing Projects". Consider using a more specific pattern like /^On .+wrote:$/i to match only email reply headers.

  2. Line 210-213: The cleaning logic only removes prefixes at the start of the line. A reply like "Thanks! Put it in Receipts/2024" would return the full sentence instead of extracting just "Receipts/2024". Consider searching for the command phrase anywhere in the line and extracting only the path portion.

These were noted in previous reviews.

🔎 Suggested improvements
 function parseFolderPath(content: string): string | null {
   const lines = content.split("\n");

   for (const line of lines) {
     const trimmed = line.trim();
-    if (trimmed && !trimmed.startsWith(">") && !trimmed.startsWith("On ")) {
+    // Skip quoted lines and typical reply headers
+    if (trimmed && !trimmed.startsWith(">") && !/^On .+wrote:$/i.test(trimmed)) {
-      // Remove common prefixes
-      const cleaned = trimmed
-        .replace(/^(put it in|move to|file to|folder:?)\s*/i, "")
+      // Look for command phrases and extract the path after them
+      const commandMatch = trimmed.match(/(?:put it in|move to|file to|folder:?)\s*(.+?)$/i);
+      const pathCandidate = commandMatch ? commandMatch[1] : trimmed;
+      
+      const cleaned = pathCandidate
         .replace(/^["']|["']$/g, "")
         .trim();

       if (cleaned) {
         return cleaned;
       }
     }
   }

   return null;
 }

106-119: Critical: Add null check for filing.driveConnection before use.

The filing.driveConnection from the Prisma include (line 65) can be null. Passing it to createDriveProviderWithRefresh at line 108 will cause a runtime error when the function attempts to destructure the connection object at line 59 of provider.ts. This was flagged in previous reviews.

🔎 Proposed fix
   try {
+    if (!filing.driveConnection) {
+      logger.error("Filing has no drive connection");
+      await prisma.documentFiling.update({
+        where: { id: filing.id },
+        data: { status: "ERROR" },
+      });
+      return;
+    }
+
     const driveProvider = await createDriveProviderWithRefresh(
       filing.driveConnection,
       logger,
     );

147-152: Consider handling missing message-id more gracefully.

Using an empty string for headerMessageId when message.headers["message-id"] is missing can break reply threading in the confirmation email. Consider either making the field optional in the confirmation flow or logging a warning when the header is absent. This was noted in previous reviews.

🔎 Suggested improvement
     const sourceMessage = {
       threadId: message.threadId,
-      headerMessageId: message.headers["message-id"] || "",
+      headerMessageId: message.headers["message-id"],
       references: message.headers.references,
     };
+
+    if (!sourceMessage.headerMessageId) {
+      logger.warn("Message-ID header missing, reply threading may not work correctly");
+    }

Note: This requires updating the sendCorrectionConfirmation function signature to accept headerMessageId?: string instead of requiring it.


121-145: Status set to FILED before file operation completes.

The filing status is updated to "FILED" at line 131, but the file moving (line 123 TODO) and upload (line 143 TODO) operations are not implemented. This creates inconsistent database state where a filing is marked as successfully filed but the file hasn't been moved or uploaded.

Consider using an intermediate status (e.g., "PENDING_MOVE") until the file operation is implemented, or defer the status update until the operation completes. This was flagged in previous reviews.

🔎 Suggested approach
     await prisma.documentFiling.update({
       where: { id: filing.id },
       data: {
         folderId: targetFolder.id,
         folderPath: folderPath,
-        status: "FILED",
+        status: filing.fileId ? "PENDING_MOVE" : "PENDING",
         wasCorrected: filing.status === "FILED",
         originalPath: filing.wasCorrected
           ? filing.originalPath
           : filing.folderPath,
         correctedAt: new Date(),
       },
     });

Then update to "FILED" only after the move/upload operation succeeds.

apps/web/utils/drive/provider.ts (3)

45-45: Use SafeError for user-facing error message.

This error can surface to users when an unsupported provider type is encountered. The same issue exists at line 85 and was flagged in previous reviews.

🔎 Proposed fix
-  throw new Error(`Unsupported drive provider: ${provider}`);
+  throw new SafeError(
+    "Unable to access your drive. Please reconnect your drive and try again.",
+  );

Based on learnings, use SafeError for error responses to prevent information disclosure.


295-300: Critical: Add user scoping to database update to prevent IDOR vulnerability.

The Prisma update uses only connectionId in the WHERE clause without validating ownership. Per coding guidelines, ALL database queries must be scoped to the authenticated user/account. This is the same issue flagged in previous reviews.

Update the function signature to accept emailAccountId and include it in the WHERE clause: where: { id: connectionId, emailAccountId }. Update all call sites at lines 129, 141, 151, 211, 223, and 233 to pass the emailAccountId from the connection object.

🔎 Proposed fix
-async function markDriveConnectionAsDisconnected(connectionId: string) {
+async function markDriveConnectionAsDisconnected(
+  connectionId: string,
+  emailAccountId: string,
+) {
   await prisma.driveConnection.update({
-    where: { id: connectionId },
+    where: { id: connectionId, emailAccountId },
     data: { isConnected: false },
   });
 }

Then update call sites to pass emailAccountId, for example at line 129:

-    await markDriveConnectionAsDisconnected(connectionId);
+    await markDriveConnectionAsDisconnected(connectionId, connection.emailAccountId);

Note: The refreshMicrosoftDriveToken and refreshGoogleDriveToken function signatures will need to be updated to include emailAccountId in the connection pick type.

Based on learnings, ALL database queries MUST be scoped to the authenticated user/account by including user/account filtering in WHERE clauses.


255-293: Critical: Add user scoping to database update to prevent IDOR vulnerability.

The Prisma update at lines 276-286 uses only connectionId in the WHERE clause without validating ownership. Per coding guidelines, ALL database queries must be scoped to the authenticated user/account to prevent Insecure Direct Object References.

Update the function signature to accept emailAccountId and include it in the WHERE clause: where: { id: connectionId, emailAccountId }. Update all call sites at lines 159 and 241 to pass the emailAccountId from the connection object.

🔎 Proposed fix
 async function saveDriveTokens({
   tokens,
   connectionId,
+  emailAccountId,
   logger,
 }: {
   tokens: {
     access_token?: string;
     refresh_token?: string;
     expires_at?: number;
   };
   connectionId: string;
+  emailAccountId: string;
   logger: Logger;
 }) {
   if (!tokens.access_token) {
     logger.warn("No access token to save for drive connection", {
       connectionId,
     });
     return;
   }

   try {
     await prisma.driveConnection.update({
-      where: { id: connectionId },
+      where: { id: connectionId, emailAccountId },
       data: {
         accessToken: tokens.access_token,
         ...(tokens.refresh_token && { refreshToken: tokens.refresh_token }),
         expiresAt: tokens.expires_at
           ? new Date(tokens.expires_at * 1000)
           : null,
         isConnected: true,
       },
     });

Then update the call sites at lines 159 and 241:

   await saveDriveTokens({
     tokens: {
       access_token: tokens.access_token,
       refresh_token: tokens.refresh_token,
       expires_at: Number.isFinite(expiresIn)
         ? Math.floor(Date.now() / 1000 + expiresIn)
         : undefined,
     },
     connectionId,
+    emailAccountId: connection.emailAccountId,
     logger,
   });

Based on learnings, ALL database queries MUST be scoped to the authenticated user/account by including user/account filtering in WHERE clauses.

🧹 Nitpick comments (3)
apps/web/components/kibo-ui/tree/index.tsx (1)

267-267: Remove unnecessary type cast.

The as ReactNode cast is unnecessary since ComponentProps<typeof motion.div> already includes proper typing for children.

🔎 Suggested simplification
-      {children as ReactNode}
+      {children}
apps/web/app/(app)/[emailAccountId]/drive/FilingActivity.tsx (1)

29-29: Consider handling errors from drive connections fetch.

While the component gracefully degrades when connectionsData is undefined (using an empty array fallback at line 56), connection fetch errors are silently ignored. Consider displaying a warning or falling back more explicitly when drive connections fail to load but filings succeed.

🔎 Example implementation
 export function FilingActivity() {
   const { data, isLoading, error } = useFilingActivity({
     limit: 10,
     offset: 0,
   });
-  const { data: connectionsData } = useDriveConnections();
+  const { data: connectionsData, error: connectionsError } = useDriveConnections();

   return (
     <div>
       <SectionHeader className="mb-3">Recent Activity</SectionHeader>
+      {connectionsError && (
+        <div className="mb-2 text-sm text-amber-600 dark:text-amber-400">
+          Unable to load drive connections. External links may not be available.
+        </div>
+      )}
       <LoadingContent loading={isLoading} error={error}>
apps/web/utils/drive/handle-filing-reply.ts (1)

179-188: Remove unnecessary optional chaining.

The extractEmailAddress function returns string (empty string for invalid input), not a nullable type. The optional chaining at line 186 is unnecessary. However, consider adding a guard for when extractEmailAddress returns an empty string to make the intent clearer.

🔎 Proposed refactor
 function verifyUserSentEmail({
   message,
   userEmail,
 }: {
   message: ParsedMessage;
   userEmail: string;
 }): boolean {
-  const fromEmail = extractEmailAddress(message.headers.from)?.toLowerCase();
-  return fromEmail === userEmail.toLowerCase();
+  const fromEmail = extractEmailAddress(message.headers.from).toLowerCase();
+  if (!fromEmail) {
+    return false;
+  }
+  return fromEmail === userEmail.toLowerCase();
 }

Comment on lines +345 to +348
const result = await createDriveFolderAction(emailAccountId, {
...data,
driveConnectionId,
});
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Action call uses incorrect signature.

createDriveFolderAction is called with emailAccountId as the first parameter. Like the other action calls in this file, actionClient should provide emailAccountId via context automatically. The call should be createDriveFolderAction({ ...data, driveConnectionId }) without the emailAccountId parameter.

Based on learnings, actionClient provides emailAccountId via ctx automatically.

🤖 Prompt for AI Agents
In apps/web/app/(app)/[emailAccountId]/drive/AllowedFolders.tsx around lines
345-348, the call to createDriveFolderAction incorrectly passes emailAccountId
as the first positional argument; remove that first argument and call
createDriveFolderAction with a single payload object (createDriveFolderAction({
...data, driveConnectionId })) so the actionClient can supply emailAccountId via
ctx automatically; update the call accordingly and ensure no other code relies
on the positional parameter.

Comment on lines +33 to +35
if (!accessToken) {
throw new Error("No access token available for drive connection");
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Use SafeError for user-facing error message.

This error can surface to users through createDriveProviderWithRefresh. Use SafeError to prevent information disclosure, consistent with the error handling at lines 62-64 and throughout the file.

🔎 Proposed fix
   if (!accessToken) {
-    throw new Error("No access token available for drive connection");
+    throw new SafeError(
+      "Unable to access your drive. Please reconnect your drive and try again.",
+    );
   }

Based on learnings, use SafeError for error responses to prevent information disclosure.

🤖 Prompt for AI Agents
In apps/web/utils/drive/provider.ts around lines 33 to 35, the code throws a
plain Error when no access token is available — replace that with throwing a
SafeError to avoid leaking internal details to users; update the throw to `throw
new SafeError("No access token available for drive connection")` and add the
appropriate import for SafeError from the project's error utilities (matching
other uses in this file, e.g., lines 62-64) so the file compiles and remains
consistent.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

This PR is being reviewed by Cursor Bugbot

Details

You are on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

hasFolders={foldersData ? foldersData.savedFolders.length > 0 : false}
phase={displayPhase}
onPreviewClick={handlePreviewClick}
/>
Copy link

Choose a reason for hiding this comment

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

Form shows empty value instead of saved filing prompt

The SetupRulesForm component is rendered immediately with initialPrompt={emailAccount?.filingPrompt || ""}. Since emailAccount data is fetched asynchronously via useEmailAccountFull(), on first render it's undefined, causing initialPrompt to be an empty string. The form's defaultValues in react-hook-form are only applied once at mount, so when the data loads, the form doesn't update to show the saved prompt. Compare this to FilingRulesForm which correctly wraps the form in LoadingContent to wait for data. Users returning to setup will see an empty form instead of their saved filing rules.

Additional Locations (1)

Fix in Cursor Fix in Web

[key]: { status: "error", error: "Unknown error" },
}));
}
});
Copy link

Choose a reason for hiding this comment

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

Missing error handler causes preview flow to get stuck

The fileAttachmentAction calls inside the onSuccess callback use .then() without a .catch() handler. If a network error or unexpected exception causes the promise to reject, the error is unhandled and the attachment's state remains permanently stuck at "filing" status. Since anyFiling checks if any attachment is still in "filing" status, the "Looks good, start auto-filing" button stays disabled indefinitely, preventing users from completing the setup flow.

Fix in Cursor Fix in Web

@baz-reviewer
Copy link
Contributor

baz-reviewer bot commented Dec 29, 2025

This change request has over 100 files and exceeds the review limit. Consider splitting it into smaller CRs for faster and more effective reviews

@rsnodgrass
Copy link
Contributor

@elie222 I love this feature, this is going to be awesome!

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 (1)
apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx (1)

37-52: Consider moving helper function to the bottom of the file.

The getProviderInfo helper function is currently defined before the main component. Per coding guidelines, helper functions should be added to the bottom of files for consistency.

📝 Suggested refactor

Move the getProviderInfo function to after the component definition (after line 213).

📜 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 c955629 and e89be0b.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (13)
  • README.md
  • apps/web/app/(app)/[emailAccountId]/assistant/ProcessRules.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsx
  • apps/web/app/(landing)/components/page.tsx
  • apps/web/components/SideNav.tsx
  • apps/web/package.json
  • apps/web/utils/email/google.ts
  • apps/web/utils/email/microsoft.ts
  • apps/web/utils/email/reply-all.test.ts
  • apps/web/utils/email/reply-all.ts
  • apps/web/utils/email/types.ts
  • package.json
✅ Files skipped from review due to trivial changes (1)
  • apps/web/utils/email/reply-all.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • apps/web/utils/email/google.ts
  • apps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ProcessRules.tsx
  • apps/web/package.json
  • apps/web/utils/email/microsoft.ts
🧰 Additional context used
📓 Path-based instructions (26)
**/*.{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/utils/email/reply-all.test.ts
  • apps/web/utils/email/types.ts
  • apps/web/components/SideNav.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
  • apps/web/app/(landing)/components/page.tsx
**/*.{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/utils/email/reply-all.test.ts
  • apps/web/utils/email/types.ts
  • apps/web/components/SideNav.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
  • apps/web/app/(landing)/components/page.tsx
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

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

apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Do not export types/interfaces that are only used within the same file. Export later if needed

Files:

  • apps/web/utils/email/reply-all.test.ts
  • apps/web/utils/email/types.ts
  • apps/web/components/SideNav.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
  • apps/web/app/(landing)/components/page.tsx
**/*.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/utils/email/reply-all.test.ts
  • apps/web/utils/email/types.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/utils/email/reply-all.test.ts
  • apps/web/utils/email/types.ts
  • apps/web/components/SideNav.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
  • apps/web/app/(landing)/components/page.tsx
**/*.{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/utils/email/reply-all.test.ts
  • apps/web/utils/email/types.ts
  • apps/web/components/SideNav.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
  • apps/web/app/(landing)/components/page.tsx
**/*.{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/utils/email/reply-all.test.ts
  • apps/web/utils/email/types.ts
  • apps/web/components/SideNav.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
  • apps/web/app/(landing)/components/page.tsx
**/*.{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/utils/email/reply-all.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/utils/email/reply-all.test.ts
  • apps/web/utils/email/types.ts
  • package.json
  • apps/web/components/SideNav.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
  • README.md
  • apps/web/app/(landing)/components/page.tsx
**/*.{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/utils/email/reply-all.test.ts
  • apps/web/utils/email/types.ts
  • apps/web/components/SideNav.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
  • apps/web/app/(landing)/components/page.tsx
**/{utils,helpers,lib}/**/*.{ts,tsx}

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

Logger should be passed as a parameter to helper functions instead of creating their own logger instances

Files:

  • apps/web/utils/email/reply-all.test.ts
  • apps/web/utils/email/types.ts
apps/web/**/*.{ts,tsx,js,jsx}

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

apps/web/**/*.{ts,tsx,js,jsx}: Use @/ path aliases for imports from project root
Prefer self-documenting code over comments; use descriptive variable and function names instead of explaining intent with comments
Add helper functions to the bottom of files, not the top
All imports go at the top of files, no mid-file dynamic imports

Files:

  • apps/web/utils/email/reply-all.test.ts
  • apps/web/utils/email/types.ts
  • apps/web/components/SideNav.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
  • apps/web/app/(landing)/components/page.tsx
apps/web/**/*.{ts,tsx,js,jsx,json,css}

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

Format code with Prettier

Files:

  • apps/web/utils/email/reply-all.test.ts
  • apps/web/utils/email/types.ts
  • apps/web/components/SideNav.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
  • apps/web/app/(landing)/components/page.tsx
apps/web/**/*.test.{ts,tsx}

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

Co-locate test files next to source files (e.g., utils/example.test.ts). Only E2E and AI tests go in __tests__/

Files:

  • apps/web/utils/email/reply-all.test.ts
apps/web/**/*.{example,ts,json}

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

Add environment variables to .env.example, env.ts, and turbo.json

Files:

  • apps/web/utils/email/reply-all.test.ts
  • apps/web/utils/email/types.ts
**/*.test.{js,jsx,ts,tsx}

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

Co-locate test files next to source files (e.g., utils/example.test.ts). Only E2E and AI tests go in __tests__/

Files:

  • apps/web/utils/email/reply-all.test.ts
**/*.test.{ts,tsx}

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

**/*.test.{ts,tsx}: Use vitest as the testing framework
Colocate test files next to the tested file with .test.ts or .test.tsx naming convention (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 the provided mock from @/utils/__mocks__/prisma
Use test helper functions getEmail, getEmailAccount, and getRule from @/__tests__/helpers for creating mock data
Clear all mocks between tests using beforeEach(() => { vi.clearAllMocks(); })
Use descriptive test names that clearly indicate what is being tested
Do not mock the Logger in tests

Files:

  • apps/web/utils/email/reply-all.test.ts
**/package.json

📄 CodeRabbit inference engine (.cursor/rules/installing-packages.mdc)

Use pnpm as the package manager

Files:

  • package.json
apps/web/components/**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/fullstack-workflow.mdc)

Use LoadingContent component to consistently handle loading and error states, passing loading, error, and children props

Use PascalCase for component file names (e.g., components/Button.tsx)

Files:

  • apps/web/components/SideNav.tsx
**/{pages,routes,components}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/gmail-api.mdc)

Never call Gmail API directly from routes or components - always use wrapper functions from the utils folder

Files:

  • apps/web/components/SideNav.tsx
  • apps/web/app/(landing)/components/page.tsx
**/*.tsx

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

**/*.tsx: Use the LoadingContent component to handle loading states instead of manual loading state management
For text areas, use the Input component with type='text', autosizeTextarea prop set to true, and registerProps for form integration

Files:

  • apps/web/components/SideNav.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
  • apps/web/app/(landing)/components/page.tsx
**/*.{jsx,tsx}

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

**/*.{jsx,tsx}: Don't use unnecessary fragments
Don't pass children as props
Don't use the return value of React.render
Make sure all dependencies are correctly specified in React hooks
Make sure all React hooks are called from the top level of component functions
Don't forget key props in iterators and collection literals
Don't define React components inside other components
Don't use event handlers on non-interactive elements
Don't assign to React component props
Don't use both children and dangerouslySetInnerHTML props on the same element
Don't use dangerous JSX props
Don't use Array index in keys
Don't insert comments as text nodes
Don't assign JSX properties multiple times
Don't add extra closing tags for components without children
Use <>...</> instead of <Fragment>...</Fragment>
Watch out for possible "wrong" semicolons inside JSX elements
Make sure void (self-closing) elements don't have children
Don't use target="_blank" without rel="noopener"
Don't use <img> elements in Next.js projects
Don't use <head> elements in Next.js projects

Files:

  • apps/web/components/SideNav.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
  • apps/web/app/(landing)/components/page.tsx
apps/web/**/*.{tsx,jsx}

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

apps/web/**/*.{tsx,jsx}: Follow tailwindcss patterns with prettier-plugin-tailwindcss for class sorting
Prefer functional components with hooks in React
Use shadcn/ui components when available
Ensure responsive design with mobile-first approach in components
Follow consistent naming conventions using PascalCase for components
Use LoadingContent component for async data with loading and error states
Use React Hook Form with Zod validation for form handling
Use result?.serverError with toastError and toastSuccess for error handling in forms

Files:

  • apps/web/components/SideNav.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
  • apps/web/app/(landing)/components/page.tsx
apps/web/app/(app)/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)

apps/web/app/(app)/**/*.{ts,tsx}: Components for the page are either put in page.tsx, or in the apps/web/app/(app)/PAGE_NAME folder
If we're in a deeply nested component we will use swr to fetch via API
If you need to use onClick in a component, that component is a client component and file must start with use client

Files:

  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
apps/web/app/**/*.{ts,tsx}

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

Follow NextJS app router structure with (app) directory

Files:

  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
  • apps/web/app/(landing)/components/page.tsx
*.md

📄 CodeRabbit inference engine (.cursor/rules/task-list.mdc)

*.md: Create task lists in markdown files named TASKS.md or with a descriptive feature-specific name (e.g., ASSISTANT_CHAT.md) in the project root to track project progress
Structure task list markdown files with sections: Feature Name Implementation (title), description, Completed Tasks, In Progress Tasks, Future Tasks, Implementation Plan, and Relevant Files subsections
Update task list markdown files by marking tasks as completed with [x], adding new identified tasks, and moving tasks between Completed/In Progress/Future sections as appropriate
Keep the 'Relevant Files' section in task list markdown files updated with file paths that have been created or modified, brief descriptions of each file's purpose, and status indicators (e.g., ✅) for completed components

Files:

  • README.md
🧠 Learnings (41)
📚 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/utils/email/reply-all.test.ts
📚 Learning: 2026-01-01T10:42:29.767Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.767Z
Learning: Applies to **/*.test.{ts,tsx} : Use test helper functions `getEmail`, `getEmailAccount`, and `getRule` from `@/__tests__/helpers` for creating mock data

Applied to files:

  • apps/web/utils/email/reply-all.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 vitest imports (`describe`, `expect`, `test`, `vi`, `beforeEach`) in LLM test files

Applied to files:

  • apps/web/utils/email/reply-all.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/utils/email/reply-all.test.ts
  • apps/web/utils/email/types.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/utils/email/reply-all.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/utils/email/reply-all.test.ts
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/**/*.test.{ts,tsx} : Co-locate test files next to source files (e.g., `utils/example.test.ts`). Only E2E and AI tests go in `__tests__/`

Applied to files:

  • apps/web/utils/email/reply-all.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/utils/email/reply-all.test.ts
  • apps/web/utils/email/types.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/utils/email/reply-all.test.ts
  • apps/web/components/SideNav.tsx
📚 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,utils/llms,__tests__}/**/*.ts : 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

Applied to files:

  • apps/web/utils/email/reply-all.test.ts
📚 Learning: 2025-11-25T14:36:45.807Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/environment-variables.mdc:0-0
Timestamp: 2025-11-25T14:36:45.807Z
Learning: Applies to turbo.json : Add new environment variables to `turbo.json` under `tasks.build.env` as a global dependency for the build task

Applied to files:

  • package.json
📚 Learning: 2025-11-25T14:36:43.454Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/environment-variables.mdc:0-0
Timestamp: 2025-11-25T14:36:43.454Z
Learning: Applies to turbo.json : Add environment variables to `turbo.json` under `tasks.build.env` array to declare build-time dependencies

Applied to files:

  • package.json
📚 Learning: 2025-11-25T14:38:56.992Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:56.992Z
Learning: Use Turborepo with pnpm workspaces for project dependency management

Applied to files:

  • package.json
📚 Learning: 2025-11-25T14:37:45.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/installing-packages.mdc:0-0
Timestamp: 2025-11-25T14:37:45.660Z
Learning: Applies to **/package.json : Use `pnpm` as the package manager

Applied to files:

  • package.json
📚 Learning: 2025-11-25T14:38:56.992Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:56.992Z
Learning: Applies to apps/web/components/ui/**/*.tsx : Shadcn UI components are located in `components/ui` directory

Applied to files:

  • apps/web/components/SideNav.tsx
  • apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:37:30.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/hooks.mdc:0-0
Timestamp: 2025-11-25T14:37:30.660Z
Learning: Applies to apps/web/hooks/use*.ts : Create dedicated hooks for specific data types (e.g., `useAccounts`, `useLabels`) that wrap `useSWR`, handle the API endpoint URL, and return data, loading state, error state, and the `mutate` function

Applied to files:

  • apps/web/components/SideNav.tsx
📚 Learning: 2025-11-25T14:37:35.343Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/hooks.mdc:0-0
Timestamp: 2025-11-25T14:37:35.343Z
Learning: Applies to apps/web/hooks/use*.ts : Create dedicated hooks for specific data types (e.g., `useAccounts`, `useLabels`) to wrap `useSWR` for individual API endpoints

Applied to files:

  • apps/web/components/SideNav.tsx
📚 Learning: 2025-11-25T14:38:27.988Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/posthog-feature-flags.mdc:0-0
Timestamp: 2025-11-25T14:38:27.988Z
Learning: Applies to apps/web/hooks/useFeatureFlags.ts : For early access features, create boolean flag hooks using `useFeatureFlagEnabled` with the pattern `export function use[FeatureName]Enabled()`

Applied to files:

  • apps/web/components/SideNav.tsx
📚 Learning: 2025-11-25T14:38:32.328Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/posthog-feature-flags.mdc:0-0
Timestamp: 2025-11-25T14:38:32.328Z
Learning: Applies to **/*.{ts,tsx} : For early access feature flags, create hooks using the naming convention `use[FeatureName]Enabled` that return a boolean from `useFeatureFlagEnabled("flag-key")`

Applied to files:

  • apps/web/components/SideNav.tsx
📚 Learning: 2025-11-25T14:38:27.988Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/posthog-feature-flags.mdc:0-0
Timestamp: 2025-11-25T14:38:27.988Z
Learning: Applies to apps/web/hooks/useFeatureFlags.ts : Use `use[FeatureName]Enabled` naming convention for boolean feature flag hooks and `use[FeatureName]Variant` for A/B test variant hooks

Applied to files:

  • apps/web/components/SideNav.tsx
📚 Learning: 2025-11-25T14:37:35.343Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/hooks.mdc:0-0
Timestamp: 2025-11-25T14:37:35.343Z
Learning: Applies to apps/web/hooks/use*.ts : Use the `use` prefix for custom hook filenames (e.g., `useAccounts.ts`)

Applied to files:

  • apps/web/components/SideNav.tsx
📚 Learning: 2025-11-25T14:37:30.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/hooks.mdc:0-0
Timestamp: 2025-11-25T14:37:30.660Z
Learning: Applies to apps/web/hooks/use*.ts : Custom hooks must use the `use` prefix in their filename (e.g., `useAccounts.ts`)

Applied to files:

  • apps/web/components/SideNav.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/**/*.{tsx,jsx} : Use LoadingContent component for async data with loading and error states

Applied to files:

  • apps/web/components/SideNav.tsx
  • apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:40:13.649Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-11-25T14:40:13.649Z
Learning: Applies to **/*.{tsx,ts,jsx,js} : Use the `LoadingContent` component to handle loading states

Applied to files:

  • apps/web/components/SideNav.tsx
📚 Learning: 2025-11-25T14:40:15.063Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-11-25T14:40:15.063Z
Learning: Applies to **/*.{tsx,ts} : Use `next/image` package for images

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
📚 Learning: 2025-11-25T14:40:13.649Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-11-25T14:40:13.649Z
Learning: Applies to **/*.{tsx,ts,jsx,js} : Use `next/image` package for images

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
📚 Learning: 2025-11-25T14:38:56.992Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:56.992Z
Learning: Components with `onClick` handlers must be client components marked with the `use client` directive

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
  • apps/web/app/(landing)/components/page.tsx
📚 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 **/*.{jsx,tsx} : Don't use `<img>` elements in Next.js projects

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
📚 Learning: 2024-08-23T11:37:26.779Z
Learnt from: aryanprince
Repo: elie222/inbox-zero PR: 210
File: apps/web/app/(app)/stats/NewsletterModal.tsx:2-4
Timestamp: 2024-08-23T11:37:26.779Z
Learning: `MoreDropdown` is a React component and `useUnsubscribeButton` is a custom React hook, and they should not be imported using `import type`.

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx
📚 Learning: 2025-11-25T14:38:56.992Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:56.992Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Create new pages at `apps/web/app/(app)/PAGE_NAME/page.tsx` with components either colocated in the same folder or in `page.tsx`

Applied to files:

  • apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:38:23.265Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:23.265Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Create new pages at `apps/web/app/(app)/PAGE_NAME/page.tsx`

Applied to files:

  • apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:38:23.265Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:23.265Z
Learning: Applies to apps/web/app/(app)/**/*.{ts,tsx} : Components for the page are either put in `page.tsx`, or in the `apps/web/app/(app)/PAGE_NAME` folder

Applied to files:

  • apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:38:18.874Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:18.874Z
Learning: Applies to apps/web/app/(app)/**/page.tsx : Create new pages at `apps/web/app/(app)/PAGE_NAME/page.tsx`

Applied to files:

  • apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:38:18.874Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:18.874Z
Learning: Applies to apps/web/app/(app)/**/*.tsx : Components for pages are either put in `page.tsx`, or in the `apps/web/app/(app)/PAGE_NAME` folder

Applied to files:

  • apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/**/*.{tsx,jsx} : Use shadcn/ui components when available

Applied to files:

  • apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:40:13.649Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-11-25T14:40:13.649Z
Learning: Applies to **/*.{tsx,ts,jsx,js} : Use Shadcn UI and Tailwind for components and styling

Applied to files:

  • apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:38:18.874Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:18.874Z
Learning: Applies to apps/web/app/(app)/**/*.tsx : If you need to use `onClick` in a component, that component must be a client component and file must start with `use client` directive

Applied to files:

  • apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:40:15.063Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-11-25T14:40:15.063Z
Learning: Applies to **/*.{tsx,ts} : Use Shadcn UI and Tailwind for components and styling

Applied to files:

  • apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:38:23.265Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:23.265Z
Learning: Applies to apps/web/app/(app)/**/*.{ts,tsx} : If you need to use `onClick` in a component, that component is a client component and file must start with `use client`

Applied to files:

  • apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:37:09.306Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-11-25T14:37:09.306Z
Learning: Applies to apps/web/components/**/*.tsx : Use `LoadingContent` component to consistently handle loading and error states, passing `loading`, `error`, and `children` props

Applied to files:

  • apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:40:13.649Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-11-25T14:40:13.649Z
Learning: Applies to **/*.{tsx,ts,jsx,js} : Implement responsive design with Tailwind CSS using a mobile-first approach

Applied to files:

  • apps/web/app/(landing)/components/page.tsx
🧬 Code graph analysis (2)
apps/web/utils/email/types.ts (1)
apps/web/utils/types.ts (1)
  • ParsedMessage (51-73)
apps/web/components/SideNav.tsx (1)
apps/web/hooks/useFeatureFlags.ts (1)
  • useSmartFilingEnabled (20-23)
⏰ 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 (10)
README.md (3)

46-47: Feature list entries are clear and follow existing patterns.

The additions to the Features section correctly document the two new capabilities (Meeting Briefs and Smart Filing) with appropriate beta/early-access labels that set proper user expectations.


162-162: Google Drive integration setup documentation is complete and consistent.

The callback URI, scope, and API enablement follow the established patterns used for Google Calendar integration. The conditional notes ("only required for Google Drive integration") align with the documentation style.

Also applies to: 181-181, 195-195


242-242: OneDrive integration setup documentation matches Microsoft OAuth pattern.

The callback URI and Files.ReadWrite permission follow the structure of existing Microsoft Graph integration documentation and are consistent with OneDrive's requirements.

Also applies to: 272-272

apps/web/utils/email/reply-all.test.ts (1)

2-6: LGTM! Formatting improvement.

The import statement has been reformatted to multiline with trailing commas, which improves readability and makes future diffs cleaner when adding or removing imports. This is a Prettier-driven formatting change with no functional impact.

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

184-190: LGTM! Method signature follows interface conventions.

The new getMessagesWithAttachments method is well-designed and consistent with existing pagination methods in the interface. The signature is properly typed, uses optional parameters appropriately, and includes pagination support via pageToken.

apps/web/components/SideNav.tsx (1)

19-19: LGTM! Clean feature flag integration.

The Smart Filing navigation item is correctly gated by the useSmartFilingEnabled() hook, following the established pattern for feature-flagged items. The implementation is consistent with existing conditional navigation items (Integrations, Meeting Briefs) and properly updates the memoization dependencies.

Key strengths:

  • Feature flag hook follows the use[FeatureName]Enabled naming convention
  • Correct React hooks usage (top-level, proper dependencies)
  • Uses prefixPath helper consistently
  • Marked as beta: true, appropriate for early-access features
  • Type-safe and accessible

Also applies to: 56-56, 83-83, 132-141, 153-159

apps/web/app/(app)/[emailAccountId]/calendars/CalendarConnectionCard.tsx (1)

3-3: LGTM! Import deduplication improves code organization.

Moving the Image import to the top of the file follows best practices and the coding guideline that all imports should be at the top of files.

apps/web/app/(landing)/components/page.tsx (2)

4-12: LGTM: Expanded imports support the new component demonstrations.

The additional Card subcomponents (CardHeader, CardContent, CardFooter, CardTitle, CardDescription) and Typography components (PageSubHeading, MutedText) are properly imported to support the enhanced component showcase sections below.

Also applies to: 16-16, 24-24


76-76: LGTM: Consistent use of MutedText component.

The replacement of inline muted text styling with the MutedText component improves consistency and maintainability across the component showcase. This follows good component composition practices.

Also applies to: 80-80, 264-266, 273-273, 280-280, 422-424, 614-614, 620-620, 627-627, 648-648, 727-727, 739-739, 751-751, 765-767, 782-784

package.json (1)

25-25: Update @turbo/gen to match turbo version for monorepo compatibility.

The current versions are misaligned: turbo@2.7.1 is paired with @turbo/gen@2.6.2. Per Turborepo's documentation, @turbo/gen versions should be kept aligned with turbo in the monorepo root to avoid TypeScript type mismatches and tooling surprises. Update @turbo/gen to 2.7.1 to match (or 2.7.2 which is now the latest for both packages).

⛔ Skipped due to learnings
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:56.992Z
Learning: Use Turborepo with pnpm workspaces for project dependency management
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/installing-packages.mdc:0-0
Timestamp: 2025-11-25T14:37:45.660Z
Learning: Applies to **/package.json : Use `pnpm` as the package manager
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/environment-variables.mdc:0-0
Timestamp: 2025-11-25T14:36:43.454Z
Learning: Applies to turbo.json : Add environment variables to `turbo.json` under `tasks.build.env` array to declare build-time dependencies
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/environment-variables.mdc:0-0
Timestamp: 2025-11-25T14:36:45.807Z
Learning: Applies to turbo.json : Add new environment variables to `turbo.json` under `tasks.build.env` as a global dependency for the build task

Comment on lines +202 to +210
if (result.success && result.filing) {
filings.push({
filingId: result.filing.id,
filename: result.filing.filename,
folderPath: result.filing.folderPath,
fileId: result.filing.fileId,
filedAt: new Date().toISOString(),
provider: result.filing.provider as DriveProviderType,
});
Copy link
Contributor

Choose a reason for hiding this comment

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

result.filing.provider as DriveProviderType is unsafe. It can pass unexpected providers to clients. Consider guarding the value at runtime and skip (or handle) unknown providers before pushing the result.

Suggested change
if (result.success && result.filing) {
filings.push({
filingId: result.filing.id,
filename: result.filing.filename,
folderPath: result.filing.folderPath,
fileId: result.filing.fileId,
filedAt: new Date().toISOString(),
provider: result.filing.provider as DriveProviderType,
});
if (result.success && result.filing) {
if (result.filing.provider !== "google" && result.filing.provider !== "microsoft") {
logger.warn("Unknown drive provider in filing preview", { provider: result.filing.provider });
continue;
}
filings.push({
filingId: result.filing.id,
filename: result.filing.filename,
folderPath: result.filing.folderPath,
fileId: result.filing.fileId,
filedAt: new Date().toISOString(),
provider: result.filing.provider as DriveProviderType,
});

🚀 Want me to fix this? Reply ex: "fix it for me".


return {
filings,
noAttachmentsFound: filings.length === 0,
Copy link
Contributor

Choose a reason for hiding this comment

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

noAttachmentsFound uses filings.length === 0, so it can report no attachments even when attachments existed but filing failed. Consider using messagesWithAttachments.length === 0 to only signal when none were found.

Suggested change
noAttachmentsFound: filings.length === 0,
noAttachmentsFound: messagesWithAttachments.length === 0,

🚀 Want me to fix this? Reply ex: "fix it for me".

connectionId,
status: response.status,
});
await markDriveConnectionAsDisconnected(connectionId);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggestion: markDriveConnectionAsDisconnected(...) can throw and hide the original SafeError. Wrap calls in try/catch and log failures so the disconnect error never replaces the root cause; always propagate the original user-facing error.

Suggested change
await markDriveConnectionAsDisconnected(connectionId);
try {
await markDriveConnectionAsDisconnected(connectionId);
} catch (err) {
logger.error("Failed to mark drive connection as disconnected", {
connectionId,
error: err,
});
}

🚀 Want me to fix this? Reply ex: "fix it for me".

id: item.id ?? "",
name,
parentId: item.parentReference?.id ?? undefined,
path: item.parentReference?.path
Copy link
Contributor

Choose a reason for hiding this comment

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

convertToFolder builds path from item.parentReference.path, which uses Graph’s internal format (e.g., /drive/root:/...). This can look wrong for display. Consider normalizing to a human-readable path (strip the /drive/root: prefix and colon semantics), or document this behavior to set expectations.

🚀 Want me to fix this? Reply ex: "fix it for me".

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: 3

♻️ Duplicate comments (1)
apps/web/prisma/schema.prisma (1)

1023-1024: Make folderPath nullable for consistency with folderId.

A past review identified that folderId and folderPath should be nullable for documents in PENDING or PREVIEW status that haven't yet had a destination determined. While folderId was made nullable, folderPath remains required. This inconsistency could cause issues when creating preview records before the AI determines the folder.

🔎 Proposed change
   // Result (after filing or prediction)
   folderId   String?  // null if AI suggests creating a new folder
-  folderPath String
+  folderPath String?
   fileId     String?
🧹 Nitpick comments (1)
apps/web/utils/webhook/validate-webhook-account.ts (1)

110-114: Consider including hasFilingEnabled in the return type for consistency.

The hasFilingEnabled flag is computed during validation but not included in the ValidatedWebhookAccount return type. While callers can derive this from emailAccount.filingEnabled and emailAccount.filingPrompt, including it would be consistent with how hasAutomationRules is handled and avoid recomputation.

🔎 Proposed change
 export type ValidatedWebhookAccount = {
   emailAccount: NonNullable<ValidatedWebhookAccountData>;
   hasAutomationRules: boolean;
+  hasFilingEnabled: boolean;
   hasAiAccess: boolean;
 };
   return {
     success: true,
     data: {
       emailAccount,
       hasAutomationRules,
+      hasFilingEnabled,
       hasAiAccess: userHasAiAccess,
     },
   };

Also applies to: 197-204

📜 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 e89be0b and 7270769.

📒 Files selected for processing (4)
  • apps/web/app/(app)/[emailAccountId]/assistant/group/ViewLearnedPatterns.tsx
  • apps/web/app/(landing)/login/page.tsx
  • apps/web/prisma/schema.prisma
  • apps/web/utils/webhook/validate-webhook-account.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/web/app/(landing)/login/page.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/group/ViewLearnedPatterns.tsx
🧰 Additional context used
📓 Path-based instructions (14)
**/*.{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/utils/webhook/validate-webhook-account.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/utils/webhook/validate-webhook-account.ts
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

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

apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Do not export types/interfaces that are only used within the same file. Export later if needed

Files:

  • apps/web/utils/webhook/validate-webhook-account.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/utils/webhook/validate-webhook-account.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/utils/webhook/validate-webhook-account.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/utils/webhook/validate-webhook-account.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/utils/webhook/validate-webhook-account.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/utils/webhook/validate-webhook-account.ts
  • apps/web/prisma/schema.prisma
**/*.{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/utils/webhook/validate-webhook-account.ts
**/{utils,helpers,lib}/**/*.{ts,tsx}

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

Logger should be passed as a parameter to helper functions instead of creating their own logger instances

Files:

  • apps/web/utils/webhook/validate-webhook-account.ts
apps/web/**/*.{ts,tsx,js,jsx}

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

apps/web/**/*.{ts,tsx,js,jsx}: Use @/ path aliases for imports from project root
Prefer self-documenting code over comments; use descriptive variable and function names instead of explaining intent with comments
Add helper functions to the bottom of files, not the top
All imports go at the top of files, no mid-file dynamic imports

Files:

  • apps/web/utils/webhook/validate-webhook-account.ts
apps/web/**/*.{ts,tsx,js,jsx,json,css}

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

Format code with Prettier

Files:

  • apps/web/utils/webhook/validate-webhook-account.ts
apps/web/**/*.{example,ts,json}

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

Add environment variables to .env.example, env.ts, and turbo.json

Files:

  • apps/web/utils/webhook/validate-webhook-account.ts
**/prisma/schema.prisma

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

Use PostgreSQL as the database system with Prisma

Files:

  • apps/web/prisma/schema.prisma
🧠 Learnings (2)
📚 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/utils/webhook/validate-webhook-account.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/utils/webhook/validate-webhook-account.ts
⏰ 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). (3)
  • GitHub Check: test
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Macroscope - Correctness Check
🔇 Additional comments (5)
apps/web/utils/webhook/validate-webhook-account.ts (2)

24-25: LGTM!

The new filing configuration fields are correctly added to the query.


181-185: LGTM!

The validation logic correctly allows webhook processing when either automation rules or filing is configured. The use of !!filingPrompt appropriately treats empty strings as disabled.

apps/web/prisma/schema.prisma (3)

994-1010: LGTM!

The FilingFolder model is well-structured with appropriate constraints and indices.


1018-1020: Verify whether duplicate filings for the same attachment are intentional.

The DocumentFiling model has no unique constraint on messageId + attachmentId, which means the same attachment could be filed multiple times. This could be intentional to support:

  1. Preview records (status PREVIEW) followed by actual filing (status FILED)
  2. Correction workflows where the same attachment is filed to different folders

However, without constraints or application-level checks, this could lead to unintended duplicate filings.

Consider adding a composite unique constraint if duplicates are not intended, or document the design decision if they are intentional:

@@unique([emailAccountId, messageId, attachmentId, status])

Or verify in the application logic that duplicate filings are properly handled.

Also applies to: 1048-1051


1180-1186: LGTM!

The DocumentFilingStatus enum has clear, well-commented values that support the filing workflow.

Comment on lines +148 to +149
filingEnabled Boolean @default(false)
filingPrompt String?
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Use @db.Text for the filingPrompt field.

The filingPrompt field is intended for natural-language filing rules and instructions, which can be lengthy. Similar fields like about, writingStyle, and coldEmailPrompt use @db.Text to avoid PostgreSQL's default varchar length limits.

🔎 Proposed change
   filingEnabled Boolean @default(false)
-  filingPrompt  String?
+  filingPrompt  String? @db.Text
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
filingEnabled Boolean @default(false)
filingPrompt String?
filingEnabled Boolean @default(false)
filingPrompt String? @db.Text
🤖 Prompt for AI Agents
In apps/web/prisma/schema.prisma around lines 148-149, the filingPrompt field
should use @db.Text because it may contain long natural-language instructions;
update the filingPrompt declaration to include the Prisma native type annotation
@db.Text (matching how about, writingStyle, and coldEmailPrompt are defined) so
the column is created as TEXT in PostgreSQL and can hold arbitrary-length
content.

Comment on lines +979 to +980
accessToken String?
refreshToken String?
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Use @db.Text for token fields.

OAuth access and refresh tokens can exceed PostgreSQL's default varchar length. The Account model (lines 24, 26) uses @db.Text for these fields, and DriveConnection should follow the same pattern for consistency and reliability.

🔎 Proposed change
   provider     String // "google" or "microsoft"
   email        String // can differ from emailAccount - e.g. connect work Drive to personal email
-  accessToken  String?
-  refreshToken String?
+  accessToken  String? @db.Text
+  refreshToken String? @db.Text
   expiresAt    DateTime?
   isConnected  Boolean   @default(true)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
accessToken String?
refreshToken String?
provider String // "google" or "microsoft"
email String // can differ from emailAccount - e.g. connect work Drive to personal email
accessToken String? @db.Text
refreshToken String? @db.Text
expiresAt DateTime?
isConnected Boolean @default(true)
🤖 Prompt for AI Agents
In apps/web/prisma/schema.prisma around lines 979-980, the DriveConnection
model's accessToken and refreshToken are plain String? but should use the
PostgreSQL text column type like the Account model; update both fields to
String? @db.Text to match Account and handle long OAuth tokens, then run prisma
format/generate and create a migration to apply the schema change.

folderId String? // null if AI suggests creating a new folder
folderPath String
fileId String?
reasoning String?
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Use @db.Text for the reasoning field.

The reasoning field stores AI-generated explanations for filing decisions, which can be lengthy. Use @db.Text to avoid PostgreSQL's default varchar length limits.

🔎 Proposed change
   fileId     String?
-  reasoning  String?
+  reasoning  String? @db.Text
   confidence Float?
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
reasoning String?
fileId String?
reasoning String? @db.Text
confidence Float?
🤖 Prompt for AI Agents
In apps/web/prisma/schema.prisma around line 1026, the reasoning String? field
should use the PostgreSQL text type to accommodate potentially long AI-generated
explanations; update the field declaration to add the @db.Text attribute (i.e.,
change it to a nullable String with @db.Text), then run prisma migrate dev (or
create a migration) and prisma generate to apply the schema change and update
the client.

@elie222
Copy link
Owner Author

elie222 commented Jan 5, 2026

@elie222 I love this feature, this is going to be awesome!

Glad you like it! Hope to get it live this week!

elie222 and others added 2 commits January 7, 2026 02:37
Without the prompt: "consent" parameter, Microsoft OAuth may not return
a refresh token on re-authentication, causing token refresh failures.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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.

4 issues found across 91 files (changes from recent commits).

Note: This PR contains a large number of files. cubic only reviews up to 75 files per PR, so some files may not have been reviewed.

Prompt for AI agents (all issues)

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


<file name=".devcontainer/setup.sh">

<violation number="1">
P2: Suppressing stderr with `2>/dev/null` hides all error messages from prisma, making it difficult to debug setup failures. Consider removing the suppression or using a more targeted approach to handle expected non-fatal warnings.</violation>
</file>

<file name="apps/web/components/CommandK.tsx">

<violation number="1">
P2: Compose command is missing from the command palette. The refactored `actionCommands` only includes Archive when a thread is selected, but the Compose action was removed entirely. Users can still use the `C` keyboard shortcut, but the command is no longer discoverable in the palette UI.</violation>
</file>

<file name="apps/web/app/(app)/[emailAccountId]/assistant/AllRulesDisabledBanner.tsx">

<violation number="1">
P2: `rules.every()` returns `true` for empty arrays (vacuous truth). This will show the "all rules are disabled" banner even when there are no rules at all, which is misleading.</violation>
</file>

<file name=".devcontainer/docker-compose.yml">

<violation number="1">
P2: Hardcoded `SRH_TOKEN` may cause token mismatch with app's `UPSTASH_REDIS_TOKEN`. Use variable interpolation to ensure consistency.

(Based on your team's feedback about cross-service env var dependency for redis tokens.) [FEEDBACK_USED]</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a 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: 2

🤖 Fix all issues with AI agents
In @apps/web/utils/drive/client.ts:
- Around line 95-99: The function exchangeMicrosoftDriveCode does not validate
env.MICROSOFT_TENANT_ID before using it; update exchangeMicrosoftDriveCode to
check that env.MICROSOFT_TENANT_ID is present alongside env.MICROSOFT_CLIENT_ID
and env.MICROSOFT_CLIENT_SECRET and throw a clear Error (e.g., "Microsoft login
not enabled - missing credentials or tenant id") if any are missing so the code
that references MICROSOFT_TENANT_ID is safe.
- Around line 74-90: The getMicrosoftDriveOAuth2Url function checks
MICROSOFT_CLIENT_ID but not MICROSOFT_TENANT_ID, which can produce an
"undefined" tenant in the authorize URL; add a validation that throws a clear
Error when env.MICROSOFT_TENANT_ID is missing (similar to the existing
MICROSOFT_CLIENT_ID check) before constructing baseUrl, and reference
env.MICROSOFT_TENANT_ID in the error message so callers can see which env var is
misconfigured.
🧹 Nitpick comments (2)
apps/web/utils/webhook/process-history-item.ts (2)

206-225: Consider parallel processing for multiple attachments.

The current sequential processing (using await in a loop) means attachments are handled one at a time. For emails with multiple attachments, this could be slow—each attachment requires downloading, text extraction, AI analysis, and upload.

Consider using Promise.allSettled() for parallel processing, with a concurrency limiter (e.g., p-limit) to control resource usage:

🔎 Proposed parallel processing with concurrency control
+import pLimit from 'p-limit';
+
+const limit = pLimit(3); // Process max 3 attachments concurrently
+
 for (const attachment of extractableAttachments) {
-  await processAttachment({
+  limit(() => processAttachment({
     emailAccount: {
       ...emailAccount,
       filingEnabled: emailAccount.filingEnabled,
       filingPrompt: emailAccount.filingPrompt,
       email: emailAccount.email,
     },
     message: parsedMessage,
     attachment,
     emailProvider: provider,
     logger,
-  }).catch((error) => {
+  })).catch((error) => {
     logger.error("Failed to process attachment", {
       filename: attachment.filename,
       error,
     });
   });
 }

Note: This requires adding p-limit as a dependency. Alternatively, use Promise.allSettled() without rate limiting if resource consumption isn't a concern in your serverless environment.


209-214: Remove redundant property assignments.

The properties filingEnabled, filingPrompt, and email are already included in the ...emailAccount spread (as defined in the type at lines 29-32), making the explicit assignments redundant.

🔎 Proposed fix to simplify object construction
 await processAttachment({
-  emailAccount: {
-    ...emailAccount,
-    filingEnabled: emailAccount.filingEnabled,
-    filingPrompt: emailAccount.filingPrompt,
-    email: emailAccount.email,
-  },
+  emailAccount,
   message: parsedMessage,
   attachment,
   emailProvider: provider,
   logger,
 }).catch((error) => {
📜 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 e89be0b and 2aafece.

📒 Files selected for processing (14)
  • README.md
  • apps/web/app/(app)/[emailAccountId]/assistant/ProcessRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/group/ViewLearnedPatterns.tsx
  • apps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsx
  • apps/web/app/(landing)/components/page.tsx
  • apps/web/app/(landing)/login/page.tsx
  • apps/web/components/ui/card.tsx
  • apps/web/prisma/schema.prisma
  • apps/web/utils/drive/client.ts
  • apps/web/utils/email/google.ts
  • apps/web/utils/outlook/message.ts
  • apps/web/utils/webhook/process-history-item.ts
  • apps/web/utils/webhook/validate-webhook-account.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • apps/web/app/(app)/[emailAccountId]/assistant/group/ViewLearnedPatterns.tsx
  • apps/web/utils/email/google.ts
  • apps/web/app/(app)/[emailAccountId]/calendars/ConnectCalendar.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ProcessRules.tsx
  • apps/web/utils/webhook/validate-webhook-account.ts
  • apps/web/components/ui/card.tsx
🧰 Additional context used
📓 Path-based instructions (21)
*.md

📄 CodeRabbit inference engine (.cursor/rules/task-list.mdc)

*.md: Create task lists in markdown files named TASKS.md or with a descriptive feature-specific name (e.g., ASSISTANT_CHAT.md) in the project root to track project progress
Structure task list markdown files with sections: Feature Name Implementation (title), description, Completed Tasks, In Progress Tasks, Future Tasks, Implementation Plan, and Relevant Files subsections
Update task list markdown files by marking tasks as completed with [x], adding new identified tasks, and moving tasks between Completed/In Progress/Future sections as appropriate
Keep the 'Relevant Files' section in task list markdown files updated with file paths that have been created or modified, brief descriptions of each file's purpose, and status indicators (e.g., ✅) for completed components

Files:

  • README.md
!(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:

  • README.md
  • apps/web/utils/drive/client.ts
  • apps/web/utils/outlook/message.ts
  • apps/web/utils/webhook/process-history-item.ts
  • apps/web/prisma/schema.prisma
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
  • apps/web/app/(landing)/login/page.tsx
  • apps/web/app/(landing)/components/page.tsx
**/*.{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/utils/drive/client.ts
  • apps/web/utils/outlook/message.ts
  • apps/web/utils/webhook/process-history-item.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
  • apps/web/app/(landing)/login/page.tsx
  • apps/web/app/(landing)/components/page.tsx
**/*.{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/utils/drive/client.ts
  • apps/web/utils/outlook/message.ts
  • apps/web/utils/webhook/process-history-item.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
  • apps/web/app/(landing)/login/page.tsx
  • apps/web/app/(landing)/components/page.tsx
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

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

apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Do not export types/interfaces that are only used within the same file. Export later if needed

Files:

  • apps/web/utils/drive/client.ts
  • apps/web/utils/outlook/message.ts
  • apps/web/utils/webhook/process-history-item.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
  • apps/web/app/(landing)/login/page.tsx
  • apps/web/app/(landing)/components/page.tsx
**/*.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/utils/drive/client.ts
  • apps/web/utils/outlook/message.ts
  • apps/web/utils/webhook/process-history-item.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/utils/drive/client.ts
  • apps/web/utils/outlook/message.ts
  • apps/web/utils/webhook/process-history-item.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
  • apps/web/app/(landing)/login/page.tsx
  • apps/web/app/(landing)/components/page.tsx
**/*.{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/utils/drive/client.ts
  • apps/web/utils/outlook/message.ts
  • apps/web/utils/webhook/process-history-item.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
  • apps/web/app/(landing)/login/page.tsx
  • apps/web/app/(landing)/components/page.tsx
**/*.{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/utils/drive/client.ts
  • apps/web/utils/outlook/message.ts
  • apps/web/utils/webhook/process-history-item.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
  • apps/web/app/(landing)/login/page.tsx
  • apps/web/app/(landing)/components/page.tsx
**/*.{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/utils/drive/client.ts
  • apps/web/utils/outlook/message.ts
  • apps/web/utils/webhook/process-history-item.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
  • apps/web/app/(landing)/login/page.tsx
  • apps/web/app/(landing)/components/page.tsx
**/{utils,helpers,lib}/**/*.{ts,tsx}

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

Logger should be passed as a parameter to helper functions instead of creating their own logger instances

Files:

  • apps/web/utils/drive/client.ts
  • apps/web/utils/outlook/message.ts
  • apps/web/utils/webhook/process-history-item.ts
apps/web/**/*.{ts,tsx,js,jsx}

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

apps/web/**/*.{ts,tsx,js,jsx}: Use @/ path aliases for imports from project root
Prefer self-documenting code over comments; use descriptive variable and function names instead of explaining intent with comments
Add helper functions to the bottom of files, not the top
All imports go at the top of files, no mid-file dynamic imports

Files:

  • apps/web/utils/drive/client.ts
  • apps/web/utils/outlook/message.ts
  • apps/web/utils/webhook/process-history-item.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
  • apps/web/app/(landing)/login/page.tsx
  • apps/web/app/(landing)/components/page.tsx
apps/web/**/*.{ts,tsx,js,jsx,json,css}

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

Format code with Prettier

Files:

  • apps/web/utils/drive/client.ts
  • apps/web/utils/outlook/message.ts
  • apps/web/utils/webhook/process-history-item.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
  • apps/web/app/(landing)/login/page.tsx
  • apps/web/app/(landing)/components/page.tsx
apps/web/**/*.{example,ts,json}

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

Add environment variables to .env.example, env.ts, and turbo.json

Files:

  • apps/web/utils/drive/client.ts
  • apps/web/utils/outlook/message.ts
  • apps/web/utils/webhook/process-history-item.ts
**/prisma/schema.prisma

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

Use PostgreSQL as the database system with Prisma

Files:

  • apps/web/prisma/schema.prisma
apps/web/app/(app)/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)

apps/web/app/(app)/**/*.{ts,tsx}: Components for the page are either put in page.tsx, or in the apps/web/app/(app)/PAGE_NAME folder
If we're in a deeply nested component we will use swr to fetch via API
If you need to use onClick in a component, that component is a client component and file must start with use client

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
**/*.tsx

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

**/*.tsx: Use the LoadingContent component to handle loading states instead of manual loading state management
For text areas, use the Input component with type='text', autosizeTextarea prop set to true, and registerProps for form integration

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
  • apps/web/app/(landing)/login/page.tsx
  • apps/web/app/(landing)/components/page.tsx
**/*.{jsx,tsx}

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

**/*.{jsx,tsx}: Don't use unnecessary fragments
Don't pass children as props
Don't use the return value of React.render
Make sure all dependencies are correctly specified in React hooks
Make sure all React hooks are called from the top level of component functions
Don't forget key props in iterators and collection literals
Don't define React components inside other components
Don't use event handlers on non-interactive elements
Don't assign to React component props
Don't use both children and dangerouslySetInnerHTML props on the same element
Don't use dangerous JSX props
Don't use Array index in keys
Don't insert comments as text nodes
Don't assign JSX properties multiple times
Don't add extra closing tags for components without children
Use <>...</> instead of <Fragment>...</Fragment>
Watch out for possible "wrong" semicolons inside JSX elements
Make sure void (self-closing) elements don't have children
Don't use target="_blank" without rel="noopener"
Don't use <img> elements in Next.js projects
Don't use <head> elements in Next.js projects

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
  • apps/web/app/(landing)/login/page.tsx
  • apps/web/app/(landing)/components/page.tsx
apps/web/app/**/*.{ts,tsx}

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

Follow NextJS app router structure with (app) directory

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
  • apps/web/app/(landing)/login/page.tsx
  • apps/web/app/(landing)/components/page.tsx
apps/web/**/*.{tsx,jsx}

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

apps/web/**/*.{tsx,jsx}: Follow tailwindcss patterns with prettier-plugin-tailwindcss for class sorting
Prefer functional components with hooks in React
Use shadcn/ui components when available
Ensure responsive design with mobile-first approach in components
Follow consistent naming conventions using PascalCase for components
Use LoadingContent component for async data with loading and error states
Use React Hook Form with Zod validation for form handling
Use result?.serverError with toastError and toastSuccess for error handling in forms

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
  • apps/web/app/(landing)/login/page.tsx
  • apps/web/app/(landing)/components/page.tsx
**/{pages,routes,components}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/gmail-api.mdc)

Never call Gmail API directly from routes or components - always use wrapper functions from the utils folder

Files:

  • apps/web/app/(landing)/components/page.tsx
🧠 Learnings (42)
📚 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/utils/drive/client.ts
📚 Learning: 2025-11-25T14:36:45.807Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/environment-variables.mdc:0-0
Timestamp: 2025-11-25T14:36:45.807Z
Learning: Applies to apps/web/env.ts : Add client-side environment variables to `apps/web/env.ts` under the `experimental__runtimeEnv` object to enable runtime access

Applied to files:

  • apps/web/utils/drive/client.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 : Maintain consistent error response format across all API routes to avoid information disclosure while providing meaningful error feedback

Applied to files:

  • apps/web/utils/drive/client.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 `SafeError` for error responses to prevent information disclosure - provide generic messages (e.g., 'Rule not found' not 'Rule {id} does not exist for user {userId}') without revealing internal IDs or ownership details

Applied to files:

  • apps/web/utils/drive/client.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/utils/outlook/message.ts
  • apps/web/utils/webhook/process-history-item.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/utils/outlook/message.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/utils/outlook/message.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} : Make sure to pass a message value when creating a built-in error

Applied to files:

  • apps/web/utils/outlook/message.ts
📚 Learning: 2025-11-25T14:42:11.919Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-11-25T14:42:11.919Z
Learning: Applies to utils/**/*.{js,ts,jsx,tsx} : The `utils` folder contains core app logic such as Next.js Server Actions and Gmail API requests

Applied to files:

  • apps/web/utils/webhook/process-history-item.ts
📚 Learning: 2025-11-25T14:42:16.602Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-11-25T14:42:16.602Z
Learning: The `utils` folder contains core app logic such as Next.js Server Actions and Gmail API requests

Applied to files:

  • apps/web/utils/webhook/process-history-item.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/utils/webhook/process-history-item.ts
📚 Learning: 2026-01-01T10:42:29.775Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.775Z
Learning: Applies to **/*.test.{ts,tsx} : Use test helper functions `getEmail`, `getEmailAccount`, and `getRule` from `@/__tests__/helpers` for creating mock data

Applied to files:

  • apps/web/utils/webhook/process-history-item.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/utils/webhook/process-history-item.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/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
📚 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/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
📚 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 : System prompts must define the LLM's role and task specifications

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
📚 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} : Make sure ARIA properties are valid for the element's supported roles

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
📚 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} : Use semantic elements instead of role attributes in JSX

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
📚 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} : Use valid, non-abstract ARIA roles for elements with ARIA roles

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
📚 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 assign non-interactive ARIA roles to interactive HTML elements

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
📚 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} : Give all elements requiring alt text meaningful information for screen readers

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
  • apps/web/app/(landing)/login/page.tsx
📚 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 add ARIA roles, states, and properties to elements that don't support them

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
📚 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} : Make sure label elements have text content and are associated with an input

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx
  • apps/web/app/(landing)/login/page.tsx
📚 Learning: 2026-01-05T14:59:55.884Z
Learnt from: jshwrnr
Repo: elie222/inbox-zero PR: 1198
File: apps/web/app/(marketing)/(alternatives)/best-fyxer-alternative/content.tsx:10-10
Timestamp: 2026-01-05T14:59:55.884Z
Learning: In apps/web/app/(marketing)/**/content.tsx files, the pattern `metadata as unknown as AlternativeComparisonProps` (or similar double casts for MDX metadata) is used consistently across marketing pages (alternatives, case-studies). This is an intentional pattern for marketing content where runtime validation overhead is not justified, and should be kept for consistency.

Applied to files:

  • apps/web/app/(landing)/login/page.tsx
  • apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:40:15.063Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-11-25T14:40:15.063Z
Learning: Applies to **/*.tsx : For text areas, use the `Input` component with `type='text'`, `autosizeTextarea` prop set to true, and `registerProps` for form integration

Applied to files:

  • apps/web/app/(landing)/login/page.tsx
📚 Learning: 2025-11-25T14:40:15.063Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-11-25T14:40:15.063Z
Learning: Applies to **/*.{tsx,ts} : Use Shadcn UI and Tailwind for components and styling

Applied to files:

  • apps/web/app/(landing)/login/page.tsx
  • apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:38:56.992Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:56.992Z
Learning: Applies to apps/web/components/ui/**/*.tsx : Shadcn UI components are located in `components/ui` directory

Applied to files:

  • apps/web/app/(landing)/login/page.tsx
  • apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:40:13.649Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-11-25T14:40:13.649Z
Learning: Applies to **/*.{tsx,ts,jsx,js} : Use Shadcn UI and Tailwind for components and styling

Applied to files:

  • apps/web/app/(landing)/login/page.tsx
  • apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/**/*.{tsx,jsx} : Follow consistent naming conventions using PascalCase for components

Applied to files:

  • apps/web/app/(landing)/login/page.tsx
📚 Learning: 2025-11-25T14:38:56.992Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:56.992Z
Learning: Applies to apps/web/components/**/*.tsx : Use PascalCase for component file names (e.g., `components/Button.tsx`)

Applied to files:

  • apps/web/app/(landing)/login/page.tsx
📚 Learning: 2025-11-25T14:38:18.874Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:18.874Z
Learning: Applies to apps/web/app/(app)/**/*.tsx : Components for pages are either put in `page.tsx`, or in the `apps/web/app/(app)/PAGE_NAME` folder

Applied to files:

  • apps/web/app/(landing)/login/page.tsx
  • apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:36:36.276Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-11-25T14:36:36.276Z
Learning: Applies to **/*.{ts,tsx} : Import error and success toast utilities from '@/components/Toast' for displaying notifications

Applied to files:

  • apps/web/app/(landing)/login/page.tsx
📚 Learning: 2025-11-25T14:38:23.265Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:23.265Z
Learning: Applies to apps/web/app/(app)/**/*.{ts,tsx} : Components for the page are either put in `page.tsx`, or in the `apps/web/app/(app)/PAGE_NAME` folder

Applied to files:

  • apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:38:23.265Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:23.265Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Create new pages at `apps/web/app/(app)/PAGE_NAME/page.tsx`

Applied to files:

  • apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:38:18.874Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:18.874Z
Learning: Applies to apps/web/app/(app)/**/page.tsx : Create new pages at `apps/web/app/(app)/PAGE_NAME/page.tsx`

Applied to files:

  • apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:38:56.992Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:56.992Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Create new pages at `apps/web/app/(app)/PAGE_NAME/page.tsx` with components either colocated in the same folder or in `page.tsx`

Applied to files:

  • apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/**/*.{tsx,jsx} : Use shadcn/ui components when available

Applied to files:

  • apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:38:56.992Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:56.992Z
Learning: Components with `onClick` handlers must be client components marked with the `use client` directive

Applied to files:

  • apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:38:18.874Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:18.874Z
Learning: Applies to apps/web/app/(app)/**/*.tsx : If you need to use `onClick` in a component, that component must be a client component and file must start with `use client` directive

Applied to files:

  • apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:38:23.265Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-11-25T14:38:23.265Z
Learning: Applies to apps/web/app/(app)/**/*.{ts,tsx} : If you need to use `onClick` in a component, that component is a client component and file must start with `use client`

Applied to files:

  • apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-11-25T14:37:09.306Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-11-25T14:37:09.306Z
Learning: Applies to apps/web/components/**/*.tsx : Use `LoadingContent` component to consistently handle loading and error states, passing `loading`, `error`, and `children` props

Applied to files:

  • apps/web/app/(landing)/components/page.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/**/*.{tsx,jsx} : Use LoadingContent component for async data with loading and error states

Applied to files:

  • apps/web/app/(landing)/components/page.tsx
🧬 Code graph analysis (3)
apps/web/utils/drive/client.ts (1)
apps/web/utils/drive/scopes.ts (2)
  • GOOGLE_DRIVE_SCOPES (18-24)
  • MICROSOFT_DRIVE_SCOPES (4-13)
apps/web/utils/webhook/process-history-item.ts (4)
apps/web/utils/filebot/is-filebot-email.ts (1)
  • isFilebotEmail (13-38)
apps/web/utils/drive/handle-filing-reply.ts (1)
  • processFilingReply (32-173)
apps/web/utils/premium/index.ts (1)
  • hasAiAccess (99-115)
apps/web/utils/drive/filing-engine.ts (1)
  • processAttachment (65-292)
apps/web/app/(landing)/login/page.tsx (1)
apps/web/components/Typography.tsx (1)
  • MutedText (142-142)
⏰ 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). (3)
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: test
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (23)
README.md (3)

46-47: Feature list additions are well-formatted and consistent.

Both new features are properly documented with status indicators (Beta, Early Access) that signal their maturity level to users.


173-173: Google Drive OAuth documentation is complete and consistent.

The callback URI, scope, and API enablement are all properly documented and follow the existing pattern for optional integrations (similar to calendar integration).

Also applies to: 192-192, 206-206


253-253: Microsoft OAuth documentation for OneDrive is properly configured.

The callback URI and Files.ReadWrite permission are correctly documented and follow the established pattern for optional integrations.

Also applies to: 283-283

apps/web/app/(landing)/login/page.tsx (1)

12-12: LGTM! Clean refactoring to centralize muted text styling.

The replacement of <p> elements with the MutedText component improves consistency across the codebase by centralizing the styling for muted text. The spacing and alignment classes are preserved correctly.

Also applies to: 51-67, 69-79

apps/web/app/(landing)/components/page.tsx (4)

4-12: LGTM! Properly expanded imports for component demonstrations.

The expanded Card component imports and addition of PageSubHeading and MutedText to the Typography imports are appropriate for the new demonstration examples being added to this storybook page.

Also applies to: 16-16, 24-24


77-77: LGTM! Typography examples added correctly.

The addition of PageSubHeading and MutedText examples in the Typography section properly demonstrates these components alongside the existing typography elements.

Also applies to: 81-81


88-129: LGTM! Excellent demonstration of Card component size variants.

The side-by-side grid showing the default and small (size="sm") card variants effectively demonstrates the Card component's size prop feature. The examples properly showcase:

  • Different card sizes with appropriate padding
  • Complete card structure (Header, Content, Footer)
  • Responsive grid layout with md:grid-cols-2
  • Consistent button sizing matching card size

288-290: LGTM! Consistent application of MutedText component.

The replacement of plain text elements with MutedText throughout the demo sections improves consistency and aligns with the broader refactoring effort to centralize muted text styling across the codebase.

Also applies to: 297-297, 304-304, 446-448, 638-638, 644-644, 651-651, 672-672, 751-751, 763-763, 775-775, 789-791, 806-808

apps/web/app/(app)/[emailAccountId]/assistant/RulesTabNew.tsx (1)

3-3: LGTM! Clean refactoring for visual consistency.

The introduction of MutedText for the descriptive text improves visual hierarchy and aligns with the PR's broader UI standardization effort. The change is straightforward and maintains the same content while applying consistent styling.

Also applies to: 10-13

apps/web/utils/drive/client.ts (4)

12-18: LGTM!

The OAuth2 client creation using the official @googleapis/drive library is well-structured and properly utilizes environment variables for configuration.


23-31: LGTM!

Good use of access_type: "offline" and prompt: "consent" to ensure refresh tokens are returned consistently.


36-65: LGTM!

Solid implementation with proper token validation and secure ID token verification to extract the user's email.


118-127: LGTM!

Good error handling pattern—checking response.ok before parsing the success response, with a safe fallback for non-JSON error bodies.

apps/web/utils/webhook/process-history-item.ts (3)

1-1: LGTM! Imports are appropriate for the filing feature.

The imports correctly bring in Next.js 15's after for background processing and the necessary filing utilities.

Also applies to: 7-12


29-32: LGTM! Type extension is correct.

The extension properly adds the filing-related fields needed by the new functionality.


114-128: LGTM! Filebot detection and early return are correctly implemented.

The code properly delegates to isFilebotEmail, which handles multiple recipients in the To field internally (splits by comma and checks each). The early return pattern keeps the code clean.

apps/web/utils/outlook/message.ts (4)

12-12: LGTM: hasAttachments field addition is correct.

The hasAttachments field is a standard Microsoft Graph message property and is properly added to the select fields.


567-583: LGTM: Attachment processing logic is robust.

The filter on line 570 correctly ensures only file attachments with valid IDs are processed. The type guard properly narrows the type, making line 576 (attachmentId: att.id) type-safe. Default values for missing fields are appropriate.


619-625: LGTM: Internal type definition follows guidelines.

The OutlookAttachment type correctly models the Microsoft Graph attachment structure and is appropriately kept internal (not exported), following the coding guideline to avoid exporting types used only within the same file.


439-439: Fetch folder IDs to properly map message labels.

Passing empty {} for folderIds will cause messages to lack proper label information (INBOX, SENT, ARCHIVE, etc.). All other query functions in this file call getFolderIds(client, logger) and pass the result to convertMessages.

Based on past review comments, this issue has been flagged multiple times and should be addressed.

Also applies to: 453-453

Likely an incorrect or invalid review comment.

apps/web/prisma/schema.prisma (3)

994-1010: Well-designed FilingFolder model.

The model structure is solid:

  • Appropriate unique constraint on [emailAccountId, folderId] prevents duplicate folder tracking
  • Proper indexes on foreign keys for query performance
  • Correct cascade delete behavior to maintain referential integrity

1180-1186: Well-documented enum with clear state definitions.

The DocumentFilingStatus enum clearly defines all filing lifecycle states with helpful inline comments. The states logically cover preview, user decision points, success, and error cases.


1012-1052: DocumentFiling schema is properly designed; folderPath nullability concern is unfounded.

The folderPath field correctly remains non-nullable. Analysis of the filing engine reveals that folderPath is always populated before creating a DocumentFiling record, even for PENDING status documents:

  • When using existing folders: folderPath is set to folder.path || folder.name
  • When creating new folders: folderPath is set to analysis.folderPath || "Inbox Zero Filed"

The || "Inbox Zero Filed" fallback ensures a default path is always available, even when the AI hasn't explicitly determined a destination. PENDING status documents therefore always have a determined folder path ready for user confirmation.

The folderId nullability improvement is appropriate for cases where new folders are being created.

Comment on lines +74 to +90
export function getMicrosoftDriveOAuth2Url(state: string): string {
if (!env.MICROSOFT_CLIENT_ID) {
throw new Error("Microsoft login not enabled - missing client ID");
}

const baseUrl = `https://login.microsoftonline.com/${env.MICROSOFT_TENANT_ID}/oauth2/v2.0/authorize`;
const params = new URLSearchParams({
client_id: env.MICROSOFT_CLIENT_ID,
response_type: "code",
redirect_uri: `${env.NEXT_PUBLIC_BASE_URL}/api/outlook/drive/callback`,
scope: MICROSOFT_DRIVE_SCOPES.join(" "),
prompt: "consent", // Ensures refresh token is returned on re-auth
state,
});

return `${baseUrl}?${params.toString()}`;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add validation for MICROSOFT_TENANT_ID.

The function validates MICROSOFT_CLIENT_ID but uses MICROSOFT_TENANT_ID in the URL without validation. If undefined, the URL would contain the literal string "undefined", causing confusing OAuth failures.

🔎 Proposed fix
 export function getMicrosoftDriveOAuth2Url(state: string): string {
-  if (!env.MICROSOFT_CLIENT_ID) {
-    throw new Error("Microsoft login not enabled - missing client ID");
+  if (!env.MICROSOFT_CLIENT_ID || !env.MICROSOFT_TENANT_ID) {
+    throw new Error("Microsoft login not enabled - missing client ID or tenant ID");
   }
🤖 Prompt for AI Agents
In @apps/web/utils/drive/client.ts around lines 74 - 90, The
getMicrosoftDriveOAuth2Url function checks MICROSOFT_CLIENT_ID but not
MICROSOFT_TENANT_ID, which can produce an "undefined" tenant in the authorize
URL; add a validation that throws a clear Error when env.MICROSOFT_TENANT_ID is
missing (similar to the existing MICROSOFT_CLIENT_ID check) before constructing
baseUrl, and reference env.MICROSOFT_TENANT_ID in the error message so callers
can see which env var is misconfigured.

Comment on lines +95 to +99
export async function exchangeMicrosoftDriveCode(code: string) {
if (!env.MICROSOFT_CLIENT_ID || !env.MICROSOFT_CLIENT_SECRET) {
throw new Error("Microsoft login not enabled - missing credentials");
}

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add validation for MICROSOFT_TENANT_ID.

Same issue as the URL generation function—MICROSOFT_TENANT_ID is used in line 101 but not validated alongside the other credentials.

🔎 Proposed fix
 export async function exchangeMicrosoftDriveCode(code: string) {
-  if (!env.MICROSOFT_CLIENT_ID || !env.MICROSOFT_CLIENT_SECRET) {
-    throw new Error("Microsoft login not enabled - missing credentials");
+  if (!env.MICROSOFT_CLIENT_ID || !env.MICROSOFT_CLIENT_SECRET || !env.MICROSOFT_TENANT_ID) {
+    throw new Error("Microsoft login not enabled - missing credentials or tenant ID");
   }
🤖 Prompt for AI Agents
In @apps/web/utils/drive/client.ts around lines 95 - 99, The function
exchangeMicrosoftDriveCode does not validate env.MICROSOFT_TENANT_ID before
using it; update exchangeMicrosoftDriveCode to check that
env.MICROSOFT_TENANT_ID is present alongside env.MICROSOFT_CLIENT_ID and
env.MICROSOFT_CLIENT_SECRET and throw a clear Error (e.g., "Microsoft login not
enabled - missing credentials or tenant id") if any are missing so the code that
references MICROSOFT_TENANT_ID is safe.

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.

2 participants

Comments