Skip to content

Comments

Persona report#594

Merged
elie222 merged 27 commits intoelie222:mainfrom
edulelis:persona-report
Aug 14, 2025
Merged

Persona report#594
elie222 merged 27 commits intoelie222:mainfrom
edulelis:persona-report

Conversation

@edulelis
Copy link
Collaborator

@edulelis edulelis commented Jul 24, 2025

Summary by CodeRabbit

  • New Features

    • Added “Report” button on Debug page to open a new Email Report.
    • Introduced a comprehensive Email Report: executive summary, professional persona, behavior & response patterns, label analysis, and actionable recommendations with priority/difficulty badges and responsive layout.
    • Shows recent email insights, label stats, quick actions, loading states, and toast notifications.
  • Chores

    • Improved email fetching reliability, centralized delay utility, and added tracing/logging for better observability.

@vercel
Copy link

vercel bot commented Jul 24, 2025

@edulelis is attempting to deploy a commit to the Inbox Zero Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jul 24, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Adds an account-scoped "Report" debug button and page; implements a server action to fetch Gmail data and run multiple AI analyses (summarization, persona, behavior, response patterns, label optimization, recommendations); migrates several LLM calls to createGenerateObject/generateObject with trace logging and minor utility/comment tweaks.

Changes

Cohort / File(s) Summary
Debug UI & Report Page
apps/web/app/(app)/[emailAccountId]/debug/page.tsx, apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
Adds a "Report" button to debug page and a client EmailReportPage component that calls generateReportAction, manages loading/error/toast state, and renders a multi-card report UI.
Email Report Backend & Gmail Fetching
apps/web/utils/actions/report.ts, apps/web/utils/ai/report/fetch.ts
New server action generateReportAction and getEmailReportData that fetch Gmail messages, labels, signature, and templates (with retries/backoff), aggregate received/sent emails, and return datasets for AI analysis.
AI Analysis Modules
apps/web/utils/ai/report/* (generate-executive-summary.ts, summarize-emails.ts, build-user-persona.ts, analyze-email-behavior.ts, response-patterns.ts, analyze-label-optimization.ts, generate-actionable-recommendations.ts)
New AI utilities that summarize emails, build persona, analyze behavior, response patterns, and labels, and produce executive summaries and structured actionable recommendations; all use Zod schemas and the generateObject-based LLM flow with input/output tracing.
LLM Rule & SDK Migration
.cursor/rules/llm.mdc
Updates featureFunction signature to accept emailAccount: EmailAccountWithAI, replaces chatCompletionObject flow with createGenerateObject/generateObject, adds input/output trace logging and model option construction, and returns result.object.
AI Assistant / Chat Fix
apps/web/utils/ai/assistant/chat.ts
Adjusts updateRuleActionsTool to include folderName in action fields only for Microsoft providers using a conditional object spread.
Minor Utilities & Logging / Comments
apps/web/utils/ai/knowledge/writing-style.ts, apps/web/utils/get-email-from-message.ts, apps/web/utils/types.ts, apps/web/app/(landing)/components/test-action.ts
Adds trace logging after generateObject in writing-style; inserts clarifying comments for EmailForLLM/getEmailForLLM; replaces inline setTimeout delay with shared sleep utility in test-action.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant DebugPage as Debug Report Page (Client)
  participant Action as generateReportAction (Server)
  participant Report as getEmailReportData
  participant Gmail as Gmail API
  participant AI as LLM

  User->>DebugPage: Click "Generate Report"
  DebugPage->>Action: invoke with { emailAccountId }
  Action->>Report: getEmailReportData(id)
  Report->>Gmail: Fetch emails, labels, signature, templates (with retries/backoff)
  Gmail-->>Report: Data sets
  Report->>AI: aiSummarizeEmails (batched)
  AI-->>Report: Email summaries
  Report->>AI: Run analyses (executive summary, persona, behavior, responses, labels, recommendations)
  AI-->>Report: Structured analyses
  Report-->>Action: Consolidated report object
  Action-->>DebugPage: Return report
  DebugPage-->>User: Render report UI
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Possibly related PRs

  • Persona report #594: Implements the same email report feature and modifies the debug report pages and server report action modules (highly related).
  • Migrate to ai sdk v5 #592: Migrates LLM calls to the SDK object-based generation API (createGenerateObject/generateObject) and logging patterns (LLM-integration overlap).
  • Improved LLM calls #660: Introduces/aligns with generateObject-based LLM abstraction used across the new AI report modules.

Suggested reviewers

  • johnlowe399-blip

Poem

I hop through inbox fields and threads,
I gather cues where context spreads.
I summarize, sort, and mark the chart,
Bite-sized insights from every part.
Report delivered — thump, thump, hooray! 🐇📬

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.


📜 Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 930e874 and 01e0a02.

📒 Files selected for processing (9)
  • .cursor/rules/llm.mdc (3 hunks)
  • apps/web/utils/ai/assistant/chat.ts (1 hunks)
  • apps/web/utils/ai/report/analyze-email-behavior.ts (1 hunks)
  • apps/web/utils/ai/report/analyze-label-optimization.ts (1 hunks)
  • apps/web/utils/ai/report/build-user-persona.ts (1 hunks)
  • apps/web/utils/ai/report/generate-actionable-recommendations.ts (1 hunks)
  • apps/web/utils/ai/report/generate-executive-summary.ts (1 hunks)
  • apps/web/utils/ai/report/response-patterns.ts (1 hunks)
  • apps/web/utils/ai/report/summarize-emails.ts (1 hunks)
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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

🧹 Nitpick comments (3)
apps/web/app/(reports)/layout.tsx (1)

56-66: Consistent navigation for sign up

The sign up button currently redirects to /login instead of a dedicated sign up page.

Consider using a dedicated sign up route for clarity:

         <Button asChild size="sm">
-          <Link href="/login" className="flex items-center gap-1">
+          <Link href="/signup" className="flex items-center gap-1">
             <UserPlus className="h-3 w-3" />
             Sign Up
           </Link>
         </Button>
apps/web/app/(reports)/email-analysis/page.tsx (2)

146-146: Use absolute URL for API endpoint

The API endpoint should use an absolute path to ensure it works correctly in all environments.

-      const response = await fetch("/api/email-analysis", {
+      const response = await fetch("/reports/api/email-analysis", {

207-212: Consider using React Hook Form for better form handling

According to coding guidelines, forms should use React Hook Form with Zod validation.

import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";

const FormSchema = z.object({
  email: z.string().email("Please enter a valid email address"),
});

const form = useForm({
  resolver: zodResolver(FormSchema),
  defaultValues: { email: "" },
});

// Use form.handleSubmit(handleAnalyze) and form.register("email")
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 470fd4e and fc2622f.

📒 Files selected for processing (5)
  • apps/web/app/(reports)/api/email-analysis/route.ts (1 hunks)
  • apps/web/app/(reports)/api/email-summaries/route.ts (1 hunks)
  • apps/web/app/(reports)/api/gmail-labels/route.ts (1 hunks)
  • apps/web/app/(reports)/email-analysis/page.tsx (1 hunks)
  • apps/web/app/(reports)/layout.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (13)
apps/web/**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use @/ for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX

Files:

  • apps/web/app/(reports)/api/gmail-labels/route.ts
  • apps/web/app/(reports)/layout.tsx
  • apps/web/app/(reports)/api/email-summaries/route.ts
  • apps/web/app/(reports)/email-analysis/page.tsx
  • apps/web/app/(reports)/api/email-analysis/route.ts
apps/web/app/**

📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)

NextJS app router structure with (app) directory

Files:

  • apps/web/app/(reports)/api/gmail-labels/route.ts
  • apps/web/app/(reports)/layout.tsx
  • apps/web/app/(reports)/api/email-summaries/route.ts
  • apps/web/app/(reports)/email-analysis/page.tsx
  • apps/web/app/(reports)/api/email-analysis/route.ts
!{.cursor/rules/*.mdc}

📄 CodeRabbit Inference Engine (.cursor/rules/cursor-rules.mdc)

Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location

Files:

  • apps/web/app/(reports)/api/gmail-labels/route.ts
  • apps/web/app/(reports)/layout.tsx
  • apps/web/app/(reports)/api/email-summaries/route.ts
  • apps/web/app/(reports)/email-analysis/page.tsx
  • apps/web/app/(reports)/api/email-analysis/route.ts
**/*.ts

📄 CodeRabbit Inference Engine (.cursor/rules/form-handling.mdc)

**/*.ts: The same validation should be done in the server action too
Define validation schemas using Zod

Files:

  • apps/web/app/(reports)/api/gmail-labels/route.ts
  • apps/web/app/(reports)/api/email-summaries/route.ts
  • apps/web/app/(reports)/api/email-analysis/route.ts
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/logging.mdc)

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

Import Prisma in the project using import prisma from "@/utils/prisma";

**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.

Files:

  • apps/web/app/(reports)/api/gmail-labels/route.ts
  • apps/web/app/(reports)/layout.tsx
  • apps/web/app/(reports)/api/email-summaries/route.ts
  • apps/web/app/(reports)/email-analysis/page.tsx
  • apps/web/app/(reports)/api/email-analysis/route.ts
**/api/**/route.ts

📄 CodeRabbit Inference Engine (.cursor/rules/security.mdc)

**/api/**/route.ts: ALL API routes that handle user data MUST use appropriate authentication and authorization middleware (withAuth or withEmailAccount).
ALL database queries in API routes MUST be scoped to the authenticated user/account (e.g., include userId or emailAccountId in query filters).
Always validate that resources belong to the authenticated user before performing operations (resource ownership validation).
Use withEmailAccount middleware for API routes that operate on a specific email account (i.e., use or require emailAccountId).
Use withAuth middleware for API routes that operate at the user level (i.e., use or require only userId).
Use withError middleware (with proper validation) for public endpoints, custom authentication, or cron endpoints.
Cron endpoints MUST use withError middleware and validate the cron secret using hasCronSecret(request) or hasPostCronSecret(request).
Cron endpoints MUST capture unauthorized attempts with captureException and return a 401 status for unauthorized requests.
All parameters in API routes MUST be validated for type, format, and length before use.
Request bodies in API routes MUST be validated using Zod schemas before use.
All Prisma queries in API routes MUST only return necessary fields and never expose sensitive data.
Error messages in API routes MUST not leak internal information or sensitive data; use generic error messages and SafeError where appropriate.
API routes MUST use a consistent error response format, returning JSON with an error message and status code.
All findUnique and findFirst Prisma calls in API routes MUST include ownership filters (e.g., userId or emailAccountId).
All findMany Prisma calls in API routes MUST be scoped to the authenticated user's data.
Never use direct object references in API routes without ownership checks (prevent IDOR vulnerabilities).
Prevent mass assignment vulnerabilities by only allowing explicitly whitelisted fields in update operations in AP...

Files:

  • apps/web/app/(reports)/api/gmail-labels/route.ts
  • apps/web/app/(reports)/api/email-summaries/route.ts
  • apps/web/app/(reports)/api/email-analysis/route.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)

**/*.{js,jsx,ts,tsx}: Don't use elements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...

Files:

  • apps/web/app/(reports)/api/gmail-labels/route.ts
  • apps/web/app/(reports)/layout.tsx
  • apps/web/app/(reports)/api/email-summaries/route.ts
  • apps/web/app/(reports)/email-analysis/page.tsx
  • apps/web/app/(reports)/api/email-analysis/route.ts
!pages/_document.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)

!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.

Files:

  • apps/web/app/(reports)/api/gmail-labels/route.ts
  • apps/web/app/(reports)/layout.tsx
  • apps/web/app/(reports)/api/email-summaries/route.ts
  • apps/web/app/(reports)/email-analysis/page.tsx
  • apps/web/app/(reports)/api/email-analysis/route.ts
apps/web/**/*.tsx

📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)

apps/web/**/*.tsx: Follow tailwindcss patterns with prettier-plugin-tailwindcss
Prefer functional components with hooks
Use shadcn/ui components when available
Ensure responsive design with mobile-first approach
Follow consistent naming conventions (PascalCase for components)
Use LoadingContent component for async data
Use result?.serverError with toastError and toastSuccess
Use LoadingContent component to handle loading and error states consistently
Pass loading, error, and children props to LoadingContent

Files:

  • apps/web/app/(reports)/layout.tsx
  • apps/web/app/(reports)/email-analysis/page.tsx
**/*.tsx

📄 CodeRabbit Inference Engine (.cursor/rules/form-handling.mdc)

**/*.tsx: Use React Hook Form with Zod for validation
Validate form inputs before submission
Show validation errors inline next to form fields

Files:

  • apps/web/app/(reports)/layout.tsx
  • apps/web/app/(reports)/email-analysis/page.tsx
apps/web/app/**/*.tsx

📄 CodeRabbit Inference Engine (.cursor/rules/project-structure.mdc)

Components with onClick must be client components with use client directive

Files:

  • apps/web/app/(reports)/layout.tsx
  • apps/web/app/(reports)/email-analysis/page.tsx
**/*.{jsx,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)

**/*.{jsx,tsx}: Don't destructure props inside JSX components in Solid projects.
Don't use both children and dangerouslySetInnerHTML props on the same element.
Don't use Array index in keys.
Don't assign to React component props.
Don't define React components inside other components.
Don't use event handlers on non-interactive elements.
Don't assign JSX properties multiple times.
Don't add extra closing tags for components without children.
Use <>...</> instead of ....
Don't insert comments as text nodes.
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 use unnecessary fragments.
Don't pass children as props.
Use semantic elements instead of role attributes in JSX.

Files:

  • apps/web/app/(reports)/layout.tsx
  • apps/web/app/(reports)/email-analysis/page.tsx
**/*.{html,jsx,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)

**/*.{html,jsx,tsx}: Don't use or elements.
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.
Only use the scope prop on 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.
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 ARIA state and property values.
Use valid values for the autocomplete attribute on input eleme...

Files:

  • apps/web/app/(reports)/layout.tsx
  • apps/web/app/(reports)/email-analysis/page.tsx
🧠 Learnings (6)
📓 Common learnings
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-07-19T17:50:28.270Z
Learning: The `utils` folder also contains core app logic such as Next.js Server Actions and Gmail API requests.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.146Z
Learning: Organize files according to the prescribed structure: API routes in `app/api/`, validation schemas in `utils/actions/*.validation.ts`, server actions in `utils/actions/`, SWR hooks in `hooks/`, and form components in `components/`.
Learnt from: elie222
PR: elie222/inbox-zero#537
File: apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx:30-34
Timestamp: 2025-07-08T13:14:07.449Z
Learning: The clean onboarding page in apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx is intentionally Gmail-specific and should show an error for non-Google email accounts rather than attempting to support multiple providers.
apps/web/app/(reports)/api/gmail-labels/route.ts (16)

Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/app/api/**/route.ts : Use withEmailAccount for email-account-level operations

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-07-18T15:05:26.713Z
Learning: Applies to app/api/**/route.ts : Always wrap the handler with withAuth or withEmailAccount for consistent error handling and authentication in GET API routes.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.146Z
Learning: Applies to apps/web/app/api/**/route.ts : Wrap all GET API route handlers with withAuth or withEmailAccount middleware for authentication and authorization.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.146Z
Learning: Applies to apps/web/app/api/**/route.ts : Export response types from GET API routes for type-safe client usage.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-07-20T09:00:41.968Z
Learning: Applies to apps/web/app/api/**/*.{ts,js} : Review all new withError usage in API routes to ensure custom authentication is implemented where required.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-07-18T15:05:26.713Z
Learning: Applies to app/api/**/route.ts : Do not use try/catch in GET API route handlers, as withAuth and withEmailAccount handle error catching.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-07-18T15:05:26.713Z
Learning: Applies to app/api/**/route.ts : Return responses using NextResponse.json() in GET API routes.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-07-18T15:05:34.899Z
Learning: Applies to apps/web/utils/gmail/**/*.ts : Keep provider-specific implementation details isolated in the appropriate utils subfolder (e.g., 'apps/web/utils/gmail/')

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-07-18T17:27:46.389Z
Learning: Applies to /api//route.ts : ALL API routes that handle user data MUST use appropriate authentication and authorization middleware (withAuth or withEmailAccount).

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-07-18T17:27:46.389Z
Learning: Applies to /api//route.ts : Use withEmailAccount middleware for API routes that operate on a specific email account (i.e., use or require emailAccountId).

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-07-19T17:50:28.270Z
Learning: The utils folder also contains core app logic such as Next.js Server Actions and Gmail API requests.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/index.mdc:0-0
Timestamp: 2025-07-18T15:05:56.644Z
Learning: Guidelines for implementing Next.js server actions

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/index.mdc:0-0
Timestamp: 2025-07-18T15:05:56.644Z
Learning: Guidelines for implementing GET API routes in Next.js

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-07-18T15:04:44.818Z
Learning: Applies to {app,components}/**/*.{ts,tsx} : For API GET requests to server, use the swr package

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-19T17:50:22.078Z
Learning: Applies to {components,app}/**/*.tsx : For API get requests to server use the swr package

Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/app/api/**/route.ts : Export response types from GET routes

apps/web/app/(reports)/layout.tsx (10)

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-07-20T09:00:16.505Z
Learning: Applies to apps/web/app/(app)/*/ : Components for the page are either in page.tsx or in the apps/web/app/(app)/PAGE_NAME folder

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-07-18T15:07:00.269Z
Learning: Applies to apps/web/app/(app)/*/** : Components for the page are either put in page.tsx, or in the apps/web/app/(app)/PAGE_NAME folder

Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/**/*.tsx : Use LoadingContent component for async data

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-07-20T09:00:16.505Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Pages are Server components for direct data loading

Learnt from: elie222
PR: #485
File: apps/web/app/(landing)/login/page.tsx:41-43
Timestamp: 2025-06-05T09:49:12.168Z
Learning: In Next.js App Router, components that use the useSearchParams hook require a Suspense boundary to handle the asynchronous nature of search parameter access. The Suspense wrapper is necessary and should not be removed when a component uses useSearchParams.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-07-20T09:00:16.505Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Create new pages at: apps/web/app/(app)/PAGE_NAME/page.tsx

Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/**/*.tsx : Use LoadingContent component to handle loading and error states consistently

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-19T17:50:22.078Z
Learning: Applies to components/**/*.tsx : Use Shadcn UI and Tailwind for components and styling

Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/**/*.tsx : Ensure responsive design with mobile-first approach

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-07-20T09:00:41.968Z
Learning: Applies to apps/web/app/api/**/*.{ts,js} : Review all new withError usage in API routes to ensure custom authentication is implemented where required.

apps/web/app/(reports)/api/email-summaries/route.ts (12)

Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/app/api/**/route.ts : Use withEmailAccount for email-account-level operations

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-07-18T17:27:46.389Z
Learning: Applies to /api//route.ts : Use withEmailAccount middleware for API routes that operate on a specific email account (i.e., use or require emailAccountId).

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-07-18T15:05:26.713Z
Learning: Applies to app/api/**/route.ts : Return responses using NextResponse.json() in GET API routes.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.146Z
Learning: Applies to apps/web/app/api/**/route.ts : Export response types from GET API routes for type-safe client usage.

Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/app/api/**/route.ts : Do NOT use POST API routes for mutations - use server actions instead

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-07-18T15:05:26.713Z
Learning: Applies to app/api/**/route.ts : Always wrap the handler with withAuth or withEmailAccount for consistent error handling and authentication in GET API routes.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-07-18T17:27:46.389Z
Learning: Applies to /api//route.ts : ALL API routes that handle user data MUST use appropriate authentication and authorization middleware (withAuth or withEmailAccount).

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.146Z
Learning: Applies to apps/web/app/api/**/route.ts : Wrap all GET API route handlers with withAuth or withEmailAccount middleware for authentication and authorization.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-07-18T15:05:26.713Z
Learning: Applies to app/api/**/route.ts : Use Prisma for database queries in GET API routes.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-07-20T09:00:41.968Z
Learning: Applies to apps/web/app/api/**/*.{ts,js} : Review all new withError usage in API routes to ensure custom authentication is implemented where required.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-07-19T17:50:28.270Z
Learning: The utils folder also contains core app logic such as Next.js Server Actions and Gmail API requests.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/index.mdc:0-0
Timestamp: 2025-07-18T15:05:56.644Z
Learning: Guidelines for implementing Next.js server actions

apps/web/app/(reports)/email-analysis/page.tsx (18)

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-07-20T09:00:16.505Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Create new pages at: apps/web/app/(app)/PAGE_NAME/page.tsx

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-07-20T09:00:16.505Z
Learning: Applies to apps/web/app/(app)/*/ : Components for the page are either in page.tsx or in the apps/web/app/(app)/PAGE_NAME folder

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-07-20T09:00:16.505Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Pages are Server components for direct data loading

Learnt from: elie222
PR: #537
File: apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx:30-34
Timestamp: 2025-07-08T13:14:07.449Z
Learning: The clean onboarding page in apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx is intentionally Gmail-specific and should show an error for non-Google email accounts rather than attempting to support multiple providers.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-07-18T15:07:00.269Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Create new pages at: apps/web/app/(app)/PAGE_NAME/page.tsx

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Define clear interfaces for complex input/output structures in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-07-18T15:07:00.269Z
Learning: Applies to apps/web/app/(app)/*/** : Components for the page are either put in page.tsx, or in the apps/web/app/(app)/PAGE_NAME folder

Learnt from: edulelis
PR: #576
File: packages/resend/emails/digest.tsx:78-83
Timestamp: 2025-07-17T04:19:57.099Z
Learning: In packages/resend/emails/digest.tsx, the DigestEmailProps type uses [key: string]: DigestItem[] | undefined | string | Date | undefined instead of intersection types like & Record<string, DigestItem[] | undefined> due to implementation constraints. This was the initial implementation approach and cannot be changed to more restrictive typing.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-07-18T15:05:34.899Z
Learning: Applies to apps/web/utils/gmail/**/*.ts : Keep provider-specific implementation details isolated in the appropriate utils subfolder (e.g., 'apps/web/utils/gmail/')

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-07-20T09:00:16.505Z
Learning: Applies to apps/web/components/**/*.tsx : Use PascalCase for components (e.g. components/Button.tsx)

Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/**/*.tsx : Use LoadingContent component for async data

Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/**/*.tsx : Use LoadingContent component to handle loading and error states consistently

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.146Z
Learning: Applies to apps/web/components/**/*.tsx : Use the LoadingContent component to handle loading and error states consistently in data-fetching components.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-19T17:50:22.078Z
Learning: Applies to components/**/*.tsx : Use the LoadingContent component to handle loading states

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-19T17:50:22.078Z
Learning: Applies to components/**/*.tsx : Implement responsive design with Tailwind CSS using a mobile-first approach

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-07-18T15:07:00.269Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Pages are Server components so you can load data into them directly

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-19T17:50:22.078Z
Learning: Applies to components/**/*.tsx : Use Shadcn UI and Tailwind for components and styling

Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/**/*.tsx : Prefer functional components with hooks

apps/web/app/(reports)/api/email-analysis/route.ts (10)

Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/app/api/**/route.ts : Use withEmailAccount for email-account-level operations

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-07-20T09:00:41.968Z
Learning: Applies to apps/web/app/api/**/*.{ts,js} : Review all new withError usage in API routes to ensure custom authentication is implemented where required.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-07-19T17:50:28.270Z
Learning: The utils folder also contains core app logic such as Next.js Server Actions and Gmail API requests.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-07-20T09:00:41.968Z
Learning: Applies to apps/web/app/api/**/*.{ts,js} : Request bodies in API routes should use Zod schemas for validation.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.146Z
Learning: Applies to apps/web/utils/actions/**/*.ts : Use server actions (with next-safe-action) for all mutations (create/update/delete operations); do NOT use POST API routes for mutations.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Follow the standard structure for LLM-related functions: use a scoped logger, define a Zod schema for output, validate inputs early, separate system and user prompts, log inputs and outputs, call chatCompletionObject with proper configuration, and return validated results.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-07-18T15:05:26.713Z
Learning: Applies to app/api/**/route.ts : Always wrap the handler with withAuth or withEmailAccount for consistent error handling and authentication in GET API routes.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-07-18T15:05:34.899Z
Learning: Applies to apps/web/utils/gmail/**/*.ts : Keep provider-specific implementation details isolated in the appropriate utils subfolder (e.g., 'apps/web/utils/gmail/')

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.146Z
Learning: Applies to apps/web/app/api/**/route.ts : Export response types from GET API routes for type-safe client usage.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.146Z
Learning: Applies to apps/web/app/api/**/route.ts : Wrap all GET API route handlers with withAuth or withEmailAccount middleware for authentication and authorization.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Static Code Analysis Js
  • GitHub Check: Jit Security
🔇 Additional comments (4)
apps/web/app/(reports)/api/gmail-labels/route.ts (1)

1-94: Export response type for type-safe client usage

According to the coding guidelines, GET API routes should export response types for type-safe client usage.

Add a response type export at the top of the file:

export type GmailLabelsResponse = {
  labels: Array<{
    id: string;
    name: string;
    messagesTotal: number;
    messagesUnread: number;
    color: string | null;
    type: string;
  }>;
  totalLabels: number;
};
⛔ Skipped due to learnings
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.146Z
Learning: Applies to apps/web/app/api/**/route.ts : Export response types from GET API routes for type-safe client usage.
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/app/api/**/route.ts : Export response types from GET routes
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-07-18T15:05:26.713Z
Learning: Applies to app/api/**/route.ts : Infer and export the response type for GET API routes using `Awaited<ReturnType<typeof getData>>` as shown in the example.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-20T09:03:06.318Z
Learning: Applies to **/*.{ts,tsx} : Use export type for types.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-07-18T15:05:26.713Z
Learning: Applies to app/api/**/route.ts : Return responses using `NextResponse.json()` in GET API routes.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-07-18T15:05:34.899Z
Learning: Applies to apps/web/utils/gmail/**/*.ts : Keep provider-specific implementation details isolated in the appropriate utils subfolder (e.g., 'apps/web/utils/gmail/')
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-07-18T17:27:46.389Z
Learning: Applies to **/api/**/route.ts : API routes MUST use a consistent error response format, returning JSON with an error message and status code.
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/**/types/**/*.ts : Centralize types in dedicated type files when shared
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use TypeScript types for all parameters and return values in LLM-related code.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Always define a Zod schema for response validation in LLM-related functions.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/index.mdc:0-0
Timestamp: 2025-07-18T15:05:56.644Z
Learning: Guidelines for implementing Next.js server actions
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-07-19T17:50:28.270Z
Learning: The `utils` folder also contains core app logic such as Next.js Server Actions and Gmail API requests.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/index.mdc:0-0
Timestamp: 2025-07-18T15:05:56.644Z
Learning: Guidelines for implementing GET API routes in Next.js
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-07-18T15:04:44.818Z
Learning: Applies to {app,components}/**/*.{ts,tsx} : For API GET requests to server, use the `swr` package
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-19T17:50:22.078Z
Learning: Applies to {components,app}/**/*.tsx : For API get requests to server use the `swr` package
apps/web/app/(reports)/layout.tsx (1)

1-131: Well-structured layout with proper authentication

The layout correctly implements authentication and admin authorization checks, following security best practices. The UI is clean and responsive.

apps/web/app/(reports)/email-analysis/page.tsx (2)

1-627: Well-structured UI with comprehensive type safety

The component has excellent TypeScript interfaces and a clean, responsive UI layout. The report display is well-organized with clear sections and visual hierarchy.


141-166: Use SWR for API calls and LoadingContent for states

According to the coding guidelines, client components should use SWR for API GET requests and LoadingContent for handling loading/error states.

Refactor to use SWR and proper loading states:

import useSWR from "swr";
import { LoadingContent } from "@/components/LoadingContent";
import { toastError, toastSuccess } from "@/components/Toast";

// At the top of the component
const { data, error, isLoading, mutate } = useSWR(
  email ? `/api/email-analysis?email=${encodeURIComponent(email)}` : null,
  fetcher,
  { revalidateOnFocus: false }
);

const handleAnalyze = async () => {
  if (!email.trim()) return;
  
  try {
    await mutate();
    toastSuccess({ description: "Report generated successfully" });
  } catch (error) {
    toastError({ description: "Failed to generate report" });
  }
};

// In the render
return (
  <LoadingContent loading={isLoading} error={error}>
    {/* existing content */}
  </LoadingContent>
);
⛔ Skipped due to learnings
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.146Z
Learning: Applies to apps/web/hooks/**/*.ts : Use SWR for client-side data fetching and caching.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.146Z
Learning: Applies to apps/web/components/**/*.tsx : Use the `LoadingContent` component to handle loading and error states consistently in data-fetching components.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-19T17:50:22.078Z
Learning: Applies to {components,app}/**/*.tsx : For API get requests to server use the `swr` package
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/hooks/**/*.ts : Use SWR for efficient data fetching and caching
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-07-18T15:04:44.818Z
Learning: Applies to {app,components}/**/*.{ts,tsx} : For API GET requests to server, use the `swr` package
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/**/*.tsx : Use `LoadingContent` component to handle loading and error states consistently
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/hooks.mdc:0-0
Timestamp: 2025-07-18T15:05:41.705Z
Learning: Applies to apps/web/hooks/use*.{js,jsx,ts,tsx} : For fetching data from API endpoints in custom hooks, prefer using `useSWR`.
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-07-18T15:07:00.269Z
Learning: Applies to apps/web/app/(app)/*/**/**/*.tsx : If we're in a deeply nested component we will use swr to fetch via API
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-07-20T09:00:41.968Z
Learning: Applies to apps/web/app/api/**/*.{ts,js} : Review all new withError usage in API routes to ensure custom authentication is implemented where required.
Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/**/*.tsx : Use LoadingContent component for async data
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/index.mdc:0-0
Timestamp: 2025-07-18T15:05:56.644Z
Learning: Fetching data from the API using SWR

Comment on lines 558 to 639
async function fetchEmailsByQuery(
gmail: any,
query: string,
count: number,
): Promise<any[]> {
const emails: any[] = [];
let nextPageToken: string | undefined;
let retryCount = 0;
const maxRetries = 3;

while (emails.length < count && retryCount < maxRetries) {
try {
const response = await getMessages(gmail, {
query: query || undefined,
maxResults: Math.min(100, count - emails.length),
pageToken: nextPageToken,
});

if (!response.messages || response.messages.length === 0) {
break;
}

// Get full message details for each email with retry logic
const messagePromises = response.messages.map(async (message: any) => {
if (!message.id) return null;

for (let i = 0; i < 3; i++) {
try {
const messageWithPayload = await getMessage(
message.id,
gmail,
"full",
);
return parseMessage(messageWithPayload);
} catch (error) {
if (i === 2) {
console.warn(
`Failed to fetch message ${message.id} after 3 attempts:`,
error,
);
return null;
}
// Wait before retry
await new Promise((resolve) => setTimeout(resolve, 1000 * (i + 1)));
}
}
return null;
});

const messages = await Promise.all(messagePromises);
const validMessages = messages.filter((msg) => msg !== null);

emails.push(...validMessages);

nextPageToken = response.nextPageToken || undefined;
if (!nextPageToken) {
break;
}

retryCount = 0; // Reset retry count on successful request
} catch (error) {
retryCount++;
console.warn(
`Gmail API error (attempt ${retryCount}/${maxRetries}):`,
error,
);

if (retryCount >= maxRetries) {
console.error(
`Failed to fetch emails after ${maxRetries} attempts:`,
error,
);
break;
}

// Wait before retry with exponential backoff
await new Promise((resolve) => setTimeout(resolve, 2000 * retryCount));
}
}

return emails;
}
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

Code duplication: fetchEmailsByQuery function

This function is duplicated from the email-summaries route. Consider extracting it to a shared utility.

Create a shared utility file:

// apps/web/utils/gmail/fetch-emails.ts
export async function fetchEmailsByQuery(
  gmail: any,
  query: string,
  count: number,
): Promise<any[]> {
  // ... existing implementation
}

Then import and use it in both files:

-async function fetchEmailsByQuery(
-  gmail: any,
-  query: string,
-  count: number,
-): Promise<any[]> {
-  // ... implementation
-}
+import { fetchEmailsByQuery } from "@/utils/gmail/fetch-emails";
🤖 Prompt for AI Agents
In apps/web/app/(reports)/api/email-analysis/route.ts lines 558 to 639, the
fetchEmailsByQuery function is duplicated from another route. To fix this,
extract the entire fetchEmailsByQuery function into a new shared utility file,
for example, apps/web/utils/gmail/fetch-emails.ts, exporting it as a named
function. Then, remove the duplicate function from this file and import the
shared function where needed to avoid code duplication and improve
maintainability.

Comment on lines 644 to 690
async function fetchGmailLabels(gmail: any): Promise<any[]> {
try {
const response = await gmail.users.labels.list({ userId: "me" });

// Filter out system labels, keep only user-created labels
const userLabels =
response.data.labels?.filter(
(label: any) =>
label.type === "user" &&
!label.name.startsWith("CATEGORY_") &&
!label.name.startsWith("CHAT"),
) || [];

// Get detailed info for each label to get message counts
const labelsWithCounts = await Promise.all(
userLabels.map(async (label: any) => {
try {
const labelDetail = await gmail.users.labels.get({
userId: "me",
id: label.id,
});
return {
...label,
messagesTotal: labelDetail.data.messagesTotal || 0,
messagesUnread: labelDetail.data.messagesUnread || 0,
threadsTotal: labelDetail.data.threadsTotal || 0,
threadsUnread: labelDetail.data.threadsUnread || 0,
};
} catch (error) {
console.warn(`Failed to get details for label ${label.name}:`, error);
return {
...label,
messagesTotal: 0,
messagesUnread: 0,
threadsTotal: 0,
threadsUnread: 0,
};
}
}),
);

return labelsWithCounts;
} catch (error) {
console.warn("Failed to fetch Gmail labels:", error);
return [];
}
}
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

Code duplication: fetchGmailLabels function

This functionality is already implemented in the gmail-labels route and duplicated here.

Extract to a shared utility and reuse:

// apps/web/utils/gmail/labels.ts
export async function fetchGmailLabels(gmail: any): Promise<GmailLabel[]> {
  // ... existing implementation
}
🤖 Prompt for AI Agents
In apps/web/app/(reports)/api/email-analysis/route.ts lines 644 to 690, the
fetchGmailLabels function duplicates existing functionality found in the
gmail-labels route. To fix this, extract the fetchGmailLabels function into a
shared utility file such as apps/web/utils/gmail/labels.ts, then import and
reuse this utility function in both places instead of duplicating the code. This
will centralize the logic and reduce code duplication.

Comment on lines 768 to 1016
export async function POST(request: NextRequest) {
const startTime = Date.now();
const requestId = Math.random().toString(36).substring(7);

console.log(
`[${requestId}] Starting comprehensive analysis for user: ${request.url}`,
);

try {
const body = await request.json();
const { userEmail } = body;

if (!userEmail) {
console.log(`[${requestId}] Error: No userEmail provided`);
return NextResponse.json(
{ error: "Email address is required" },
{ status: 400 },
);
}

console.log(`[${requestId}] Processing analysis for: ${userEmail}`);

// Fetch user's email account
const emailAccount = await prisma.emailAccount.findFirst({
where: { user: { email: userEmail } },
include: { account: true },
});

if (!emailAccount) {
console.log(
`[${requestId}] Error: Email account not found for ${userEmail}`,
);
return NextResponse.json(
{ error: "Email account not found" },
{ status: 404 },
);
}

console.log(`[${requestId}] Found email account: ${emailAccount.email}`);

// Get Gmail client
console.log(`[${requestId}] Initializing Gmail client...`);
const gmail = await getGmailClientWithRefresh({
accessToken: emailAccount.account?.access_token ?? "",
refreshToken: emailAccount.account?.refresh_token ?? "",
expiresAt: emailAccount.account?.expires_at,
emailAccountId: emailAccount.id,
});
console.log(`[${requestId}] Gmail client initialized successfully`);

// Fetch raw emails to get date information
console.log(`[${requestId}] Fetching raw emails for date analysis...`);
const [receivedEmails, sentEmails] = await Promise.all([
fetchEmailsByQuery(gmail, "", 200),
fetchEmailsByQuery(gmail, "from:me", 50),
]);
console.log(
`[${requestId}] Fetched ${receivedEmails.length} received emails, ${sentEmails.length} sent emails`,
);

// Get date range from actual emails
const allEmails = [...receivedEmails, ...sentEmails];
const emailDates = allEmails
.map((email) =>
email.headers?.date ? new Date(email.headers.date) : null,
)
.filter((date) => date !== null)
.sort((a, b) => a!.getTime() - b!.getTime());

const oldestDate =
emailDates.length > 0
? emailDates[0]
: new Date(Date.now() - 90 * 24 * 60 * 60 * 1000);
const newestDate =
emailDates.length > 0 ? emailDates[emailDates.length - 1] : new Date();
const totalDays = Math.ceil(
(newestDate.getTime() - oldestDate.getTime()) / (24 * 60 * 60 * 1000),
);

// Fetch email summaries from the email-summaries endpoint
console.log(`[${requestId}] Fetching email summaries...`);
const emailSummariesUrl = `${request.url.replace("/comprehensive-analysis", "/email-summaries")}`;

// Create AbortController for timeout handling
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 300000); // 5 minute timeout

let receivedResponse: Response, sentResponse: Response;
try {
[receivedResponse, sentResponse] = await Promise.all([
fetch(emailSummariesUrl, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ userEmail, count: 200 }),
signal: controller.signal,
}),
fetch(emailSummariesUrl, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ userEmail, query: "from:me", count: 50 }),
signal: controller.signal,
}),
]);
} finally {
clearTimeout(timeoutId);
}

// Check for HTTP errors
if (!receivedResponse.ok) {
const errorText = await receivedResponse.text();
console.error(`[${requestId}] Received emails API error:`, {
status: receivedResponse.status,
statusText: receivedResponse.statusText,
body: errorText.substring(0, 500),
});
throw new Error(
`Failed to fetch received emails: ${receivedResponse.status} ${receivedResponse.statusText}`,
);
}

if (!sentResponse.ok) {
const errorText = await sentResponse.text();
console.error(`[${requestId}] Sent emails API error:`, {
status: sentResponse.status,
statusText: sentResponse.statusText,
body: errorText.substring(0, 500),
});
throw new Error(
`Failed to fetch sent emails: ${sentResponse.status} ${sentResponse.statusText}`,
);
}

const receivedData = await receivedResponse.json();
const sentData = await sentResponse.json();

console.log(`[${requestId}] Email summaries fetched successfully:`, {
receivedCount: receivedData.summaries?.length || 0,
sentCount: sentData.summaries?.length || 0,
});

// Fetch additional Gmail data
console.log(`[${requestId}] Fetching additional Gmail data...`);
const [gmailLabels, gmailSignature, gmailTemplates] = await Promise.all([
fetchGmailLabels(gmail),
fetchGmailSignature(gmail),
fetchGmailTemplates(gmail),
]);
console.log(`[${requestId}] Gmail data fetched:`, {
labelsCount: gmailLabels.length,
hasSignature: !!gmailSignature,
templatesCount: gmailTemplates.length,
});

// Run all analysis functions
console.log(`[${requestId}] Starting AI analysis functions...`);
const [
executiveSummary,
userPersona,
emailBehavior,
responsePatterns,
labelAnalysis,
] = await Promise.all([
generateExecutiveSummary(
receivedData.summaries,
sentData.summaries,
gmailLabels,
),
buildUserPersona(
receivedData.summaries,
sentData.summaries,
gmailSignature,
gmailTemplates,
),
analyzeEmailBehavior(receivedData.summaries, sentData.summaries),
analyzeResponsePatterns(receivedData.summaries, sentData.summaries),
analyzeLabelOptimization(receivedData.summaries, gmailLabels),
]);
console.log(`[${requestId}] AI analysis functions completed successfully`);

// Generate actionable recommendations based on all analysis
console.log(`[${requestId}] Generating actionable recommendations...`);
const actionableRecommendations = await generateActionableRecommendations(
receivedData.summaries,
userPersona,
emailBehavior,
);
console.log(`[${requestId}] Actionable recommendations generated`);

// Compile comprehensive report
console.log(`[${requestId}] Compiling final report...`);
const comprehensiveReport = {
executiveSummary: {
...executiveSummary,
keyMetrics: {
totalEmails: receivedData.totalEmails + sentData.totalEmails,
dateRange: `${totalDays} days (${oldestDate.toLocaleDateString()} - ${newestDate.toLocaleDateString()})`,
analysisFreshness: "Just now",
},
},
emailActivityOverview: {
dataSources: {
inbox: Math.floor(receivedData.totalEmails * 0.6),
archived: Math.floor(receivedData.totalEmails * 0.3),
trash: Math.floor(receivedData.totalEmails * 0.1),
sent: sentData.totalEmails,
},
},
userPersona,
emailBehavior,
responsePatterns,
labelAnalysis: {
currentLabels: gmailLabels.map((label) => ({
name: label.name,
emailCount: label.messagesTotal || 0,
unreadCount: label.messagesUnread || 0,
})),
optimizationSuggestions: labelAnalysis.optimizationSuggestions,
},
actionableRecommendations,
processingTime: Date.now() - startTime,
};

console.log(
`[${requestId}] Analysis completed successfully in ${Date.now() - startTime}ms`,
);
return NextResponse.json(comprehensiveReport);
} catch (error) {
const errorMessage =
error instanceof Error ? error.message : "Unknown error";
const errorStack = error instanceof Error ? error.stack : undefined;

console.error(`[${requestId}] Comprehensive analysis error:`, {
error: errorMessage,
stack: errorStack,
processingTime: Date.now() - startTime,
url: request.url,
});

return NextResponse.json(
{
error: "Analysis failed",
details: errorMessage,
requestId,
processingTime: Date.now() - startTime,
},
{ status: 500 },
);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Critical security vulnerability: Missing authentication middleware

This API route processes extensive email data and AI analysis without any authentication. This is a critical security vulnerability allowing unauthorized access to any user's email data.

Implement proper authentication:

+import { withEmailAccount } from "@/utils/api";
+import { createScopedLogger } from "@/utils/logger";
+
+const logger = createScopedLogger("EmailAnalysis");
+
-export async function POST(request: NextRequest) {
+export const POST = withEmailAccount(async (request) => {
   const startTime = Date.now();
   const requestId = Math.random().toString(36).substring(7);
+  const emailAccountId = request.auth.emailAccountId;

-  console.log(
-    `[${requestId}] Starting comprehensive analysis for user: ${request.url}`,
-  );
+  logger.with({ requestId }).info("Starting comprehensive analysis");

   try {
-    const body = await request.json();
-    const { userEmail } = body;
-
-    if (!userEmail) {
-      console.log(`[${requestId}] Error: No userEmail provided`);
-      return NextResponse.json(
-        { error: "Email address is required" },
-        { status: 400 },
-      );
-    }

     // Fetch user's email account
-    const emailAccount = await prisma.emailAccount.findFirst({
-      where: { user: { email: userEmail } },
+    const emailAccount = await prisma.emailAccount.findUnique({
+      where: { id: emailAccountId },
       include: { account: true },
     });

🏁 Script executed:

#!/bin/bash
# Search for withEmailAccount definition and usage
echo "Searching for withEmailAccount definition..."
rg "export (const|function) withEmailAccount" -n

echo -e "\nSearching for withEmailAccount usage in API routes..."
rg "withEmailAccount" -n apps/web/app

echo -e "\nSearching for withAuth usage in API routes..."
rg "withAuth" -n apps/web/app

echo -e "\nListing all route.ts files under apps/web/app..."
fd apps/web/app -t f -e ts | sed 's/^/  - /'

Length of output: 13502


Enforce authentication with withEmailAccount middleware

This route handles user-specific email analysis and must be wrapped in withEmailAccount to prevent unauthorized access and scope all Prisma queries to the authenticated account. Replace raw console.log calls with a scoped logger and load the emailAccountId from request.auth.

• File to update: apps/web/app/(reports)/api/email-analysis/route.ts
• Wrap the handler, import from the existing middleware and logger utilities.

+ import { withEmailAccount } from "@/utils/middleware";
+ import { createScopedLogger } from "@/utils/logger";
  
- export async function POST(request: NextRequest) {
+ const logger = createScopedLogger("EmailAnalysis");
+ export const POST = withEmailAccount(async (request) => {
     const startTime = Date.now();
     const requestId = Math.random().toString(36).substring(7);
+    const emailAccountId = request.auth.emailAccountId;
 
-    console.log(
-      `[${requestId}] Starting comprehensive analysis for user: ${request.url}`,
-    );
+    logger.with({ requestId }).info("Starting comprehensive analysis");
 
     try {
-      const body = await request.json();
-      const { userEmail } = body;
-
-      if (!userEmail) {
-        console.log(`[${requestId}] Error: No userEmail provided`);
-        return NextResponse.json(
-          { error: "Email address is required" },
-          { status: 400 },
-        );
-      }
 
       // Fetch user's email account
-      const emailAccount = await prisma.emailAccount.findFirst({
-        where: { user: { email: userEmail } },
+      const emailAccount = await prisma.emailAccount.findUnique({
+        where: { id: emailAccountId },
         include: { account: true },
       });

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In apps/web/app/(reports)/api/email-analysis/route.ts from lines 768 to 1016,
the POST handler must be wrapped with the withEmailAccount middleware to enforce
authentication and scope Prisma queries to the authenticated user's email
account. Replace all raw console.log calls with the scoped logger imported from
the logger utilities. Also, obtain the emailAccountId from request.auth provided
by the middleware instead of fetching it manually. Import withEmailAccount and
the logger at the top, then export the wrapped handler as the default export.

Comment on lines 811 to 815
accessToken: emailAccount.account?.access_token ?? "",
refreshToken: emailAccount.account?.refresh_token ?? "",
expiresAt: emailAccount.account?.expires_at,
emailAccountId: emailAccount.id,
});
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

Validate OAuth tokens before use

Using nullish coalescing with empty strings for OAuth tokens will cause authentication failures.

+    if (!emailAccount.account?.access_token || !emailAccount.account?.refresh_token) {
+      logger.with({ requestId }).error("OAuth tokens not found");
+      return NextResponse.json(
+        { error: "OAuth tokens not found" },
+        { status: 401 },
+      );
+    }
+
     const gmail = await getGmailClientWithRefresh({
-      accessToken: emailAccount.account?.access_token ?? "",
-      refreshToken: emailAccount.account?.refresh_token ?? "",
+      accessToken: emailAccount.account.access_token,
+      refreshToken: emailAccount.account.refresh_token,
       expiresAt: emailAccount.account?.expires_at,
       emailAccountId: emailAccount.id,
     });
🤖 Prompt for AI Agents
In apps/web/app/(reports)/api/email-analysis/route.ts around lines 811 to 815,
the OAuth tokens are assigned using nullish coalescing with empty strings, which
can lead to authentication failures if tokens are missing or invalid. Instead of
defaulting to empty strings, add proper validation to check if access_token and
refresh_token exist and are valid before using them. If tokens are missing,
handle the error or trigger a token refresh flow rather than proceeding with
empty strings.

Comment on lines 848 to 870
console.log(`[${requestId}] Fetching email summaries...`);
const emailSummariesUrl = `${request.url.replace("/comprehensive-analysis", "/email-summaries")}`;

// Create AbortController for timeout handling
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 300000); // 5 minute timeout

let receivedResponse: Response, sentResponse: Response;
try {
[receivedResponse, sentResponse] = await Promise.all([
fetch(emailSummariesUrl, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ userEmail, count: 200 }),
signal: controller.signal,
}),
fetch(emailSummariesUrl, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ userEmail, query: "from:me", count: 50 }),
signal: controller.signal,
}),
]);
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

Avoid internal HTTP calls - use direct function calls

Making HTTP calls to internal API endpoints is inefficient and adds unnecessary network overhead.

Instead of making HTTP calls, extract the core logic into reusable functions:

// Extract summarization logic to a shared function
import { summarizeEmails } from "@/utils/email/summarize";

// Use the function directly
const [receivedSummaries, sentSummaries] = await Promise.all([
  summarizeEmails({ 
    gmail, 
    query: "", 
    count: 200,
    emailAccountId 
  }),
  summarizeEmails({ 
    gmail, 
    query: "from:me", 
    count: 50,
    emailAccountId 
  }),
]);
🤖 Prompt for AI Agents
In apps/web/app/(reports)/api/email-analysis/route.ts around lines 848 to 870,
the code makes internal HTTP POST requests to fetch email summaries, which is
inefficient. Refactor by extracting the email summarization logic into a
reusable function (e.g., summarizeEmails) and call this function directly with
appropriate parameters instead of making HTTP calls. Import the summarizeEmails
function and replace the fetch calls with direct calls to summarizeEmails,
passing gmail, query, count, and emailAccountId as needed.

Comment on lines 253 to 257
accessToken: emailAccount.account?.access_token!,
refreshToken: emailAccount.account?.refresh_token!,
expiresAt: emailAccount.account?.expires_at,
emailAccountId: emailAccount.id,
});
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

Validate OAuth tokens before use

Using non-null assertions on nullable OAuth tokens can lead to runtime errors.

+    if (!emailAccount.account?.access_token || !emailAccount.account?.refresh_token) {
+      logger.with({ requestId }).error("OAuth tokens not found");
+      return NextResponse.json(
+        { error: "OAuth tokens not found" },
+        { status: 401 },
+      );
+    }
+
     const gmail = await getGmailClientWithRefresh({
-      accessToken: emailAccount.account?.access_token!,
-      refreshToken: emailAccount.account?.refresh_token!,
+      accessToken: emailAccount.account.access_token,
+      refreshToken: emailAccount.account.refresh_token,
       expiresAt: emailAccount.account?.expires_at,
       emailAccountId: emailAccount.id,
     });
📝 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: emailAccount.account?.access_token!,
refreshToken: emailAccount.account?.refresh_token!,
expiresAt: emailAccount.account?.expires_at,
emailAccountId: emailAccount.id,
});
if (!emailAccount.account?.access_token || !emailAccount.account?.refresh_token) {
logger.with({ requestId }).error("OAuth tokens not found");
return NextResponse.json(
{ error: "OAuth tokens not found" },
{ status: 401 },
);
}
const gmail = await getGmailClientWithRefresh({
accessToken: emailAccount.account.access_token,
refreshToken: emailAccount.account.refresh_token,
expiresAt: emailAccount.account?.expires_at,
emailAccountId: emailAccount.id,
});
🤖 Prompt for AI Agents
In apps/web/app/(reports)/api/email-summaries/route.ts around lines 253 to 257,
the code uses non-null assertions on potentially nullable OAuth tokens, which
can cause runtime errors if the tokens are undefined or null. Modify the code to
explicitly check that access_token and refresh_token exist before using them,
and handle the case where they are missing by either throwing an error or
returning early to prevent unsafe usage.

Comment on lines 5 to 94
export async function POST(request: NextRequest) {
try {
const { userEmail } = await request.json();

if (!userEmail) {
return NextResponse.json(
{ error: "Email address is required" },
{ status: 400 },
);
}

const emailAccount = await prisma.emailAccount.findFirst({
where: { user: { email: userEmail } },
include: { account: true },
});

if (!emailAccount) {
return NextResponse.json(
{ error: "Email account not found" },
{ status: 404 },
);
}

const gmail = await getGmailClientWithRefresh({
accessToken: emailAccount.account.access_token!,
refreshToken: emailAccount.account.refresh_token!,
expiresAt: emailAccount.account.expires_at,
emailAccountId: emailAccount.id,
});

const response = await gmail.users.labels.list({ userId: "me" });

const userLabels =
response.data.labels?.filter(
(label: any) =>
label.type === "user" &&
!label.name.startsWith("CATEGORY_") &&
!label.name.startsWith("CHAT"),
) || [];

const labelsWithCounts = await Promise.all(
userLabels.map(async (label: any) => {
try {
const labelDetail = await gmail.users.labels.get({
userId: "me",
id: label.id,
});
return {
...label,
messagesTotal: labelDetail.data.messagesTotal || 0,
messagesUnread: labelDetail.data.messagesUnread || 0,
threadsTotal: labelDetail.data.threadsTotal || 0,
threadsUnread: labelDetail.data.threadsUnread || 0,
};
} catch (error) {
console.warn(`Failed to get details for label ${label.name}:`, error);
return {
...label,
messagesTotal: 0,
messagesUnread: 0,
threadsTotal: 0,
threadsUnread: 0,
};
}
}),
);

const sortedLabels = labelsWithCounts.sort(
(a: any, b: any) => (b.messagesTotal || 0) - (a.messagesTotal || 0),
);

return NextResponse.json({
labels: sortedLabels.map((label: any) => ({
id: label.id,
name: label.name,
messagesTotal: label.messagesTotal || 0,
messagesUnread: label.messagesUnread || 0,
color: label.color || null,
type: label.type,
})),
totalLabels: sortedLabels.length,
});
} catch (error) {
console.error("Gmail labels fetch error:", error);
return NextResponse.json(
{ error: "Failed to fetch Gmail labels" },
{ status: 500 },
);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Critical security vulnerability: Missing authentication and authorization middleware

This API route handles user data without any authentication or authorization. According to the coding guidelines, ALL API routes that handle user data MUST use appropriate authentication middleware (withAuth or withEmailAccount).

Additionally, this endpoint allows any user to fetch Gmail labels for any email account by simply providing an email address, which is a serious security vulnerability.

Consider refactoring this to:

  1. Use withEmailAccount middleware since it operates on email account data
  2. Remove the userEmail parameter and use the authenticated user's email account
  3. Consider using GET instead of POST for this read operation
-export async function POST(request: NextRequest) {
-  try {
-    const { userEmail } = await request.json();
-
-    if (!userEmail) {
-      return NextResponse.json(
-        { error: "Email address is required" },
-        { status: 400 },
-      );
-    }
-
-    const emailAccount = await prisma.emailAccount.findFirst({
-      where: { user: { email: userEmail } },
-      include: { account: true },
-    });
+import { withEmailAccount } from "@/utils/api";
+
+export const GET = withEmailAccount(async (request) => {
+  const emailAccountId = request.auth.emailAccountId;
+  
+  const emailAccount = await prisma.emailAccount.findUnique({
+    where: { id: emailAccountId },
+    include: { account: true },
+  });

🏁 Script executed:

#!/bin/bash
# Locate the Gmail labels route file
route_file=$(fd --hidden --follow --base-directory . -e ts "route.ts" | grep "gmail-labels/route.ts" || true)
echo "Found file: $route_file"

if [ -n "$route_file" ]; then
  echo
  echo "---- Checking for authentication middleware imports ----"
  rg -n "withAuth|withEmailAccount" "$route_file" || echo "No authentication middleware imports found"

  echo
  echo "---- Checking exported handlers ----"
  rg -n "export (const|async function) (GET|POST)" "$route_file" || echo "No GET/POST exports found"
fi

Length of output: 905


Secure the Gmail labels route with withEmailAccount middleware

This endpoint currently allows any user to fetch labels by supplying an arbitrary email address and lacks any authentication/authorization guard, violating our API security guidelines. Please refactor as follows:

• File to update:

  • apps/web/app/(reports)/api/gmail-labels/route.ts

• Required changes:

  1. Import and wrap the handler with withEmailAccount (not withAuth) since this operates on a specific email account.
  2. Remove the userEmail request body parameter—use request.auth.emailAccountId instead.
  3. Change from POST to GET for this read-only operation.

Example diff:

-import { NextRequest, NextResponse } from "next/server";
+import { NextRequest, NextResponse } from "next/server";
+import { withEmailAccount } from "@/utils/api";

-export async function POST(request: NextRequest) {
-  const { userEmail } = await request.json();
-  // … lookup by userEmail …
+export const GET = withEmailAccount(async (request) => {
+  const emailAccountId = request.auth.emailAccountId;
+  // … lookup by emailAccountId …
🤖 Prompt for AI Agents
In apps/web/app/(reports)/api/gmail-labels/route.ts lines 5 to 94, the POST
handler currently accepts a userEmail from the request body without
authentication, which is insecure. Refactor by changing the method from POST to
GET, remove the userEmail parameter from the request body, and instead use
request.auth.emailAccountId provided by the withEmailAccount middleware. Import
and wrap the handler with withEmailAccount to enforce authentication and
authorization based on the email account. Adjust the prisma query to find the
emailAccount by id using request.auth.emailAccountId rather than by user email.

Comment on lines 29 to 33
accessToken: emailAccount.account.access_token!,
refreshToken: emailAccount.account.refresh_token!,
expiresAt: emailAccount.account.expires_at,
emailAccountId: emailAccount.id,
});
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

Avoid non-null assertions on nullable fields

Using non-null assertions on potentially nullable OAuth token fields is unsafe and could lead to runtime errors.

-    const gmail = await getGmailClientWithRefresh({
-      accessToken: emailAccount.account.access_token!,
-      refreshToken: emailAccount.account.refresh_token!,
-      expiresAt: emailAccount.account.expires_at,
-      emailAccountId: emailAccount.id,
-    });
+    if (!emailAccount.account?.access_token || !emailAccount.account?.refresh_token) {
+      return NextResponse.json(
+        { error: "OAuth tokens not found" },
+        { status: 401 },
+      );
+    }
+
+    const gmail = await getGmailClientWithRefresh({
+      accessToken: emailAccount.account.access_token,
+      refreshToken: emailAccount.account.refresh_token,
+      expiresAt: emailAccount.account.expires_at,
+      emailAccountId: emailAccount.id,
+    });
📝 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: emailAccount.account.access_token!,
refreshToken: emailAccount.account.refresh_token!,
expiresAt: emailAccount.account.expires_at,
emailAccountId: emailAccount.id,
});
if (!emailAccount.account?.access_token || !emailAccount.account?.refresh_token) {
return NextResponse.json(
{ error: "OAuth tokens not found" },
{ status: 401 },
);
}
const gmail = await getGmailClientWithRefresh({
accessToken: emailAccount.account.access_token,
refreshToken: emailAccount.account.refresh_token,
expiresAt: emailAccount.account.expires_at,
emailAccountId: emailAccount.id,
});
🤖 Prompt for AI Agents
In apps/web/app/(reports)/api/gmail-labels/route.ts around lines 29 to 33,
remove the non-null assertion operators (!) from access_token and refresh_token
fields because these fields may be nullable. Instead, add proper null checks or
fallback handling before using these values to ensure the code safely handles
cases where tokens might be missing, preventing potential runtime errors.

Comment on lines 161 to 162
console.error("Error generating report:", error);
// You might want to show a toast notification here
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

Show user-friendly error messages

Errors should be displayed to users, not just logged to console.

     } catch (error) {
-      console.error("Error generating report:", error);
-      // You might want to show a toast notification here
+      toastError({ 
+        description: "Failed to generate report. Please try again." 
+      });
     } finally {
📝 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
console.error("Error generating report:", error);
// You might want to show a toast notification here
} catch (error) {
toastError({
description: "Failed to generate report. Please try again."
});
} finally {
🤖 Prompt for AI Agents
In apps/web/app/(reports)/email-analysis/page.tsx around lines 161 to 162, the
code only logs errors to the console without informing the user. Modify the
error handling to display a user-friendly error message, such as using a toast
notification or an alert component, so users are aware that an error occurred
during report generation.

Comment on lines 108 to 109
href="/sandbox"
className="flex items-center gap-2 hover:opacity-80 transition-opacity"
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Consider making the sandbox link dynamic

The hardcoded /sandbox link might not exist or might need to be configurable.


🏁 Script executed:

#!/bin/bash
# Check if /sandbox route exists in the app directory
fd -t f "page.tsx" apps/web/app | grep -E "sandbox/page.tsx|\\(reports\\)/page.tsx"

# Check for any sandbox-related routes
rg -l "sandbox" apps/web/app --type tsx --type ts | head -10

Length of output: 170


🏁 Script executed:

#!/bin/bash
# 1. Check for a sandbox directory or page under the NextJS app folder
fd sandbox apps/web/app

# 2. Look for any literal '/sandbox' references across the repo
rg -n "/sandbox" .

Length of output: 116


Action Required: Fix Broken Sandbox Link

A quick search confirms there is no apps/web/app/sandbox/page.tsx (or any other sandbox route) in the codebase, so the hardcoded href="/sandbox" will 404. Please address this by either:

  • Adding a sandbox page under apps/web/app (e.g. apps/web/app/sandbox/page.tsx),
  • Removing or conditionally hiding the link until the sandbox feature is available,
  • Extracting route paths into a shared constant (e.g. src/constants/routes.ts) and using something like href={ROUTES.sandbox} here to avoid hardcoded strings in the future.

Files to update:

  • apps/web/app/(reports)/layout.tsx (line 108)
🤖 Prompt for AI Agents
In apps/web/app/(reports)/layout.tsx around lines 108 to 109, the href attribute
is set to "/sandbox" which leads to a 404 because the sandbox route does not
exist. Fix this by either creating a sandbox page at
apps/web/app/sandbox/page.tsx, or remove or conditionally hide the link until
the sandbox feature is implemented. Additionally, consider extracting route
paths into a shared constant file like src/constants/routes.ts and use that
constant for the href to avoid hardcoded strings.

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

🧹 Nitpick comments (7)
apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx (3)

151-155: Remove unnecessary Content-Type header for GET request

The Content-Type header is not needed for GET requests as they don't have a request body.

          init: {
            method: "GET",
-            headers: {
-              "Content-Type": "application/json",
-            },
          },

142-175: Add cleanup for fetch request on component unmount

The fetch request should be cancellable if the component unmounts before the request completes.

  useEffect(() => {
    const generateReport = async () => {
      if (!emailAccountId) return;

+     const abortController = new AbortController();

      try {
        const response = await fetchWithAccount({
          url: "/api/email-report",
          emailAccountId,
          init: {
            method: "GET",
            headers: {
              "Content-Type": "application/json",
            },
+           signal: abortController.signal,
          },
        });

        if (!response.ok) {
          throw new Error(`Failed to generate report: ${response.statusText}`);
        }

        const data = await response.json();
        setReport(data);
      } catch (error) {
+       if (error instanceof Error && error.name === 'AbortError') {
+         // Request was cancelled, no need to update state
+         return;
+       }
        console.error("Error generating report:", error);
        setError(
          error instanceof Error ? error.message : "Failed to generate report",
        );
      } finally {
        setIsLoading(false);
      }
    };

    generateReport();
+   
+   return () => {
+     abortController.abort();
+   };
  }, [emailAccountId]);

286-299: Consider using stable keys instead of array indices

Using array indices as keys in React can cause issues with component state if items are reordered. Consider using unique identifiers where available.

For example, instead of:

{report.executiveSummary.quickActions
  .slice(0, 3)
  .map((action, index) => (
    <div key={index} className="flex items-center gap-2">

Consider using a combination of properties as key:

{report.executiveSummary.quickActions
  .slice(0, 3)
  .map((action, index) => (
    <div key={`${action.action}-${action.difficulty}`} className="flex items-center gap-2">

This applies to all the map functions throughout the component where index is used as the key.

Also applies to: 309-327, 349-360, 368-375, 426-433, 453-479, 486-512, 531-549, 558-585, 605-631, 640-657, 666-690

apps/web/app/(reports)/api/email-report/route.ts (1)

320-425: Consider parallelizing independent async operations

Several of the prompt generation functions could be run in parallel since they don't depend on each other's results.

// Run independent operations in parallel
const [
  executiveSummary,
  userPersona,
  emailBehavior,
  responsePatterns,
  labelAnalysis
] = await Promise.all([
  generateExecutiveSummary(
    receivedEmailSummaries,
    sentEmailSummaries,
    gmailLabels,
    userEmail,
    emailAccount,
  ).catch(error => {
    logger.error("Failed to generate executive summary", {
      error: error instanceof Error ? error.message : String(error),
      emailAccountId,
    });
    throw new Error(
      `Failed to generate executive summary: ${error instanceof Error ? error.message : String(error)}`,
    );
  }),
  buildUserPersona(
    receivedEmailSummaries,
    userEmail,
    emailAccount,
    sentEmailSummaries,
    gmailSignature,
    gmailTemplates,
  ).catch(error => {
    logger.error("Failed to build user persona", {
      error: error instanceof Error ? error.message : String(error),
      emailAccountId,
    });
    throw new Error(
      `Failed to build user persona: ${error instanceof Error ? error.message : String(error)}`,
    );
  }),
  // ... similar for other functions
]);

// Then generate actionableRecommendations which depends on userPersona and emailBehavior
const actionableRecommendations = await generateActionableRecommendations(
  receivedEmailSummaries,
  userEmail,
  emailAccount,
  userPersona,
  emailBehavior,
);

This could significantly reduce the total execution time of the report generation.

apps/web/app/(reports)/api/email-report/fetch.ts (1)

15-16: Fix typo in documentation

Minor typo in the comment.

- * This approach fetches one message at a time with retry and backofflogic, which is slower but more
+ * This approach fetches one message at a time with retry and backoff logic, which is slower but more
apps/web/app/(reports)/api/email-report/prompts.ts (2)

76-76: Add null check for label name

The label name could potentially be null/undefined.

-${gmailLabels.map((label) => `- ${label.name} (${label.messagesTotal || 0} emails)`).join("\n")}
+${gmailLabels.map((label) => `- ${label.name || 'Unnamed'} (${label.messagesTotal || 0} emails)`).join("\n")}

553-560: Consider propagating errors instead of silently returning empty array

Returning an empty array on error could hide issues and make debugging difficult. Consider either propagating the error or implementing retry logic.

  } catch (error) {
    logger.error("processEmailBatch: failed to summarize batch", {
      batchNumber,
      error,
      userEmail,
    });
-   return [];
+   // Rethrow to let caller handle the error
+   throw new Error(
+     `Failed to summarize email batch ${batchNumber}: ${
+       error instanceof Error ? error.message : String(error)
+     }`
+   );
  }

Alternatively, you could implement retry logic similar to what's done in the fetch module.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fc2622f and 7eed5dd.

📒 Files selected for processing (6)
  • apps/web/app/(app)/[emailAccountId]/debug/page.tsx (1 hunks)
  • apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx (1 hunks)
  • apps/web/app/(reports)/api/email-report/fetch.ts (1 hunks)
  • apps/web/app/(reports)/api/email-report/prompts.ts (1 hunks)
  • apps/web/app/(reports)/api/email-report/route.ts (1 hunks)
  • apps/web/app/(reports)/api/email-report/schemas.ts (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • apps/web/app/(app)/[emailAccountId]/debug/page.tsx
  • apps/web/app/(reports)/api/email-report/schemas.ts
🧰 Additional context used
📓 Path-based instructions (16)
apps/web/**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use @/ for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX

Files:

  • apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
  • apps/web/app/(reports)/api/email-report/route.ts
  • apps/web/app/(reports)/api/email-report/fetch.ts
  • apps/web/app/(reports)/api/email-report/prompts.ts
apps/web/app/**

📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)

NextJS app router structure with (app) directory

Files:

  • apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
  • apps/web/app/(reports)/api/email-report/route.ts
  • apps/web/app/(reports)/api/email-report/fetch.ts
  • apps/web/app/(reports)/api/email-report/prompts.ts
apps/web/**/*.tsx

📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)

apps/web/**/*.tsx: Follow tailwindcss patterns with prettier-plugin-tailwindcss
Prefer functional components with hooks
Use shadcn/ui components when available
Ensure responsive design with mobile-first approach
Follow consistent naming conventions (PascalCase for components)
Use LoadingContent component for async data
Use result?.serverError with toastError and toastSuccess
Use LoadingContent component to handle loading and error states consistently
Pass loading, error, and children props to LoadingContent

Files:

  • apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
!{.cursor/rules/*.mdc}

📄 CodeRabbit Inference Engine (.cursor/rules/cursor-rules.mdc)

Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location

Files:

  • apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
  • apps/web/app/(reports)/api/email-report/route.ts
  • apps/web/app/(reports)/api/email-report/fetch.ts
  • apps/web/app/(reports)/api/email-report/prompts.ts
**/*.tsx

📄 CodeRabbit Inference Engine (.cursor/rules/form-handling.mdc)

**/*.tsx: Use React Hook Form with Zod for validation
Validate form inputs before submission
Show validation errors inline next to form fields

Files:

  • apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/logging.mdc)

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

Import Prisma in the project using import prisma from "@/utils/prisma";

**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.

Files:

  • apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
  • apps/web/app/(reports)/api/email-report/route.ts
  • apps/web/app/(reports)/api/email-report/fetch.ts
  • apps/web/app/(reports)/api/email-report/prompts.ts
apps/web/app/(app)/*/**

📄 CodeRabbit Inference Engine (.cursor/rules/page-structure.mdc)

Components for the page are either put in page.tsx, or in the apps/web/app/(app)/PAGE_NAME folder

Files:

  • apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
apps/web/app/(app)/*/**/*.tsx

📄 CodeRabbit Inference Engine (.cursor/rules/page-structure.mdc)

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]/debug/report/page.tsx
apps/web/app/(app)/*/**/**/*.tsx

📄 CodeRabbit Inference Engine (.cursor/rules/page-structure.mdc)

If we're in a deeply nested component we will use swr to fetch via API

Files:

  • apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
apps/web/app/**/*.tsx

📄 CodeRabbit Inference Engine (.cursor/rules/project-structure.mdc)

Components with onClick must be client components with use client directive

Files:

  • apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)

**/*.{js,jsx,ts,tsx}: Don't use elements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...

Files:

  • apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
  • apps/web/app/(reports)/api/email-report/route.ts
  • apps/web/app/(reports)/api/email-report/fetch.ts
  • apps/web/app/(reports)/api/email-report/prompts.ts
!pages/_document.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)

!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.

Files:

  • apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
  • apps/web/app/(reports)/api/email-report/route.ts
  • apps/web/app/(reports)/api/email-report/fetch.ts
  • apps/web/app/(reports)/api/email-report/prompts.ts
**/*.{jsx,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)

**/*.{jsx,tsx}: Don't destructure props inside JSX components in Solid projects.
Don't use both children and dangerouslySetInnerHTML props on the same element.
Don't use Array index in keys.
Don't assign to React component props.
Don't define React components inside other components.
Don't use event handlers on non-interactive elements.
Don't assign JSX properties multiple times.
Don't add extra closing tags for components without children.
Use <>...</> instead of ....
Don't insert comments as text nodes.
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 use unnecessary fragments.
Don't pass children as props.
Use semantic elements instead of role attributes in JSX.

Files:

  • apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
**/*.{html,jsx,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)

**/*.{html,jsx,tsx}: Don't use or elements.
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.
Only use the scope prop on 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.
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 ARIA state and property values.
Use valid values for the autocomplete attribute on input eleme...

Files:

  • apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
**/*.ts

📄 CodeRabbit Inference Engine (.cursor/rules/form-handling.mdc)

**/*.ts: The same validation should be done in the server action too
Define validation schemas using Zod

Files:

  • apps/web/app/(reports)/api/email-report/route.ts
  • apps/web/app/(reports)/api/email-report/fetch.ts
  • apps/web/app/(reports)/api/email-report/prompts.ts
**/api/**/route.ts

📄 CodeRabbit Inference Engine (.cursor/rules/security.mdc)

**/api/**/route.ts: ALL API routes that handle user data MUST use appropriate authentication and authorization middleware (withAuth or withEmailAccount).
ALL database queries in API routes MUST be scoped to the authenticated user/account (e.g., include userId or emailAccountId in query filters).
Always validate that resources belong to the authenticated user before performing operations (resource ownership validation).
Use withEmailAccount middleware for API routes that operate on a specific email account (i.e., use or require emailAccountId).
Use withAuth middleware for API routes that operate at the user level (i.e., use or require only userId).
Use withError middleware (with proper validation) for public endpoints, custom authentication, or cron endpoints.
Cron endpoints MUST use withError middleware and validate the cron secret using hasCronSecret(request) or hasPostCronSecret(request).
Cron endpoints MUST capture unauthorized attempts with captureException and return a 401 status for unauthorized requests.
All parameters in API routes MUST be validated for type, format, and length before use.
Request bodies in API routes MUST be validated using Zod schemas before use.
All Prisma queries in API routes MUST only return necessary fields and never expose sensitive data.
Error messages in API routes MUST not leak internal information or sensitive data; use generic error messages and SafeError where appropriate.
API routes MUST use a consistent error response format, returning JSON with an error message and status code.
All findUnique and findFirst Prisma calls in API routes MUST include ownership filters (e.g., userId or emailAccountId).
All findMany Prisma calls in API routes MUST be scoped to the authenticated user's data.
Never use direct object references in API routes without ownership checks (prevent IDOR vulnerabilities).
Prevent mass assignment vulnerabilities by only allowing explicitly whitelisted fields in update operations in AP...

Files:

  • apps/web/app/(reports)/api/email-report/route.ts
🧬 Code Graph Analysis (3)
apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx (2)
apps/web/utils/fetch.ts (1)
  • fetchWithAccount (7-25)
apps/web/components/ui/card.tsx (4)
  • Card (137-137)
  • CardHeader (138-138)
  • CardTitle (140-140)
  • CardContent (142-142)
apps/web/app/(reports)/api/email-report/route.ts (7)
apps/web/utils/logger.ts (1)
  • createScopedLogger (17-65)
apps/web/utils/middleware.ts (1)
  • withEmailAccount (190-194)
apps/web/utils/types.ts (1)
  • ParsedMessage (47-58)
apps/web/app/(reports)/api/email-report/fetch.ts (2)
  • fetchEmailsForReport (214-335)
  • fetchGmailTemplates (386-415)
apps/web/app/(reports)/api/email-report/schemas.ts (1)
  • EmailSummary (160-160)
apps/web/app/(reports)/api/email-report/prompts.ts (7)
  • summarizeEmails (385-447)
  • generateExecutiveSummary (19-121)
  • buildUserPersona (123-184)
  • analyzeEmailBehavior (186-231)
  • analyzeResponsePatterns (233-292)
  • analyzeLabelOptimization (294-339)
  • generateActionableRecommendations (341-380)
apps/web/utils/gmail/client.ts (1)
  • getGmailClientWithRefresh (49-98)
apps/web/app/(reports)/api/email-report/prompts.ts (4)
apps/web/utils/logger.ts (1)
  • createScopedLogger (17-65)
apps/web/app/(reports)/api/email-report/schemas.ts (7)
  • EmailSummary (160-160)
  • executiveSummarySchema (12-51)
  • userPersonaSchema (53-63)
  • emailBehaviorSchema (65-82)
  • responsePatternsSchema (84-118)
  • labelAnalysisSchema (120-131)
  • actionableRecommendationsSchema (133-158)
apps/web/utils/llms/index.ts (1)
  • chatCompletionObject (107-111)
apps/web/utils/types.ts (1)
  • ParsedMessage (47-58)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Static Code Analysis Js
  • GitHub Check: Jit Security

Comment on lines 180 to 194
emailAccount = await prisma.emailAccount.findFirst({
where: { user: { email: userEmail } },
include: {
account: true,
user: {
select: {
email: true,
aiProvider: true,
aiModel: true,
aiApiKey: true,
},
},
},
});

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

Add ownership validation for emailAccountId

The current query only filters by user email but doesn't validate that the provided emailAccountId belongs to that user. This could potentially allow access to other users' email accounts.

    emailAccount = await prisma.emailAccount.findFirst({
-     where: { user: { email: userEmail } },
+     where: { 
+       id: emailAccountId,
+       user: { email: userEmail } 
+     },
      include: {
        account: true,
        user: {
          select: {
            email: true,
            aiProvider: true,
            aiModel: true,
            aiApiKey: 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
emailAccount = await prisma.emailAccount.findFirst({
where: { user: { email: userEmail } },
include: {
account: true,
user: {
select: {
email: true,
aiProvider: true,
aiModel: true,
aiApiKey: true,
},
},
},
});
emailAccount = await prisma.emailAccount.findFirst({
where: {
id: emailAccountId,
user: { email: userEmail },
},
include: {
account: true,
user: {
select: {
email: true,
aiProvider: true,
aiModel: true,
aiApiKey: true,
},
},
},
});
🤖 Prompt for AI Agents
In apps/web/app/(reports)/api/email-report/route.ts around lines 180 to 194, the
query fetching emailAccount only filters by user email and does not verify that
the provided emailAccountId belongs to that user. To fix this, update the
query's where clause to include a condition that matches the emailAccountId with
the provided ID and ensure it belongs to the user with the given email. This
will enforce ownership validation and prevent unauthorized access to other
users' email accounts.

Comment on lines 313 to 318
let executiveSummary: any,
userPersona: any,
emailBehavior: any,
responsePatterns: any,
labelAnalysis: any,
actionableRecommendations: any;
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

Replace 'any' types with proper type imports

Using 'any' type defeats TypeScript's type safety benefits.

-  let executiveSummary: any,
-    userPersona: any,
-    emailBehavior: any,
-    responsePatterns: any,
-    labelAnalysis: any,
-    actionableRecommendations: any;
+  let executiveSummary: Awaited<ReturnType<typeof generateExecutiveSummary>>,
+    userPersona: Awaited<ReturnType<typeof buildUserPersona>>,
+    emailBehavior: Awaited<ReturnType<typeof analyzeEmailBehavior>>,
+    responsePatterns: Awaited<ReturnType<typeof analyzeResponsePatterns>>,
+    labelAnalysis: Awaited<ReturnType<typeof analyzeLabelOptimization>>,
+    actionableRecommendations: Awaited<ReturnType<typeof generateActionableRecommendations>>;

Or better yet, import the schema types:

import type { z } from "zod";
import type { 
  executiveSummarySchema,
  userPersonaSchema,
  emailBehaviorSchema,
  responsePatternsSchema,
  labelAnalysisSchema,
  actionableRecommendationsSchema
} from "./schemas";

// Then use:
let executiveSummary: z.infer<typeof executiveSummarySchema>;
// etc.
🤖 Prompt for AI Agents
In apps/web/app/(reports)/api/email-report/route.ts around lines 313 to 318, the
variables executiveSummary, userPersona, emailBehavior, responsePatterns,
labelAnalysis, and actionableRecommendations are all typed as 'any', which
bypasses TypeScript's type safety. To fix this, import the relevant schema types
from the schemas file and use z.infer<typeof schema> to type each variable
accordingly, replacing 'any' with the inferred types from the respective
schemas.

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

🧹 Nitpick comments (3)
apps/web/app/(reports)/api/email-report/fetch.ts (3)

204-306: Solid implementation with minor improvements needed.

The function is well-structured with proper error handling and Gmail client management. Consider these small improvements:

+const API_RATE_LIMIT_DELAY = 3000; // 3 seconds between API calls
+
 export async function fetchEmailsForReport({
   emailAccount,
 }: {
   emailAccount: EmailAccount & {
     account: Account;
     user: Pick<User, "email" | "aiProvider" | "aiModel" | "aiApiKey">;
   };
 }): Promise<EmailFetchResult> {
   // ... existing code ...

-  await new Promise((resolve) => setTimeout(resolve, 3000));
+  await new Promise((resolve) => setTimeout(resolve, API_RATE_LIMIT_DELAY));

Also consider making the target counts (200, 50) configurable constants for better maintainability.


357-386: Good implementation with minor improvements suggested.

The function handles draft processing well with proper error isolation. Consider extracting the magic numbers as constants for better maintainability.

+const MAX_DRAFTS_TO_FETCH = 50;
+const MAX_TEMPLATES_TO_RETURN = 10;
+
 export async function fetchGmailTemplates(
   gmail: gmail_v1.Gmail,
 ): Promise<string[]> {
   try {
-    const drafts = await fetchEmailsByQuery(gmail, "in:draft", 50);
+    const drafts = await fetchEmailsByQuery(gmail, "in:draft", MAX_DRAFTS_TO_FETCH);

     const templates: string[] = [];

     for (const draft of drafts) {
       try {
         if (draft.textPlain?.trim()) {
           templates.push(draft.textPlain.trim());
         }

-        if (templates.length >= 10) break;
+        if (templates.length >= MAX_TEMPLATES_TO_RETURN) break;
       } catch (error) {

1-387: Overall solid implementation with good practices.

This module demonstrates good TypeScript practices, comprehensive error handling, and proper Gmail API integration. The extensive logging will be valuable for debugging production issues. The main areas for improvement are function complexity and magic number usage, but the code is production-ready.

Consider creating a constants file or configuration object for the various numeric values used throughout (retry counts, delays, batch sizes) to improve maintainability.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7eed5dd and a33bc1e.

📒 Files selected for processing (1)
  • apps/web/app/(reports)/api/email-report/fetch.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
apps/web/**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use @/ for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX

Files:

  • apps/web/app/(reports)/api/email-report/fetch.ts
apps/web/app/**

📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)

NextJS app router structure with (app) directory

Files:

  • apps/web/app/(reports)/api/email-report/fetch.ts
!{.cursor/rules/*.mdc}

📄 CodeRabbit Inference Engine (.cursor/rules/cursor-rules.mdc)

Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location

Files:

  • apps/web/app/(reports)/api/email-report/fetch.ts
**/*.ts

📄 CodeRabbit Inference Engine (.cursor/rules/form-handling.mdc)

**/*.ts: The same validation should be done in the server action too
Define validation schemas using Zod

Files:

  • apps/web/app/(reports)/api/email-report/fetch.ts
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/logging.mdc)

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

Import Prisma in the project using import prisma from "@/utils/prisma";

**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.

Files:

  • apps/web/app/(reports)/api/email-report/fetch.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)

**/*.{js,jsx,ts,tsx}: Don't use elements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...

Files:

  • apps/web/app/(reports)/api/email-report/fetch.ts
!pages/_document.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)

!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.

Files:

  • apps/web/app/(reports)/api/email-report/fetch.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). (2)
  • GitHub Check: Static Code Analysis Js
  • GitHub Check: Jit Security
🔇 Additional comments (4)
apps/web/app/(reports)/api/email-report/fetch.ts (4)

1-10: LGTM! Proper imports and logger setup.

The imports follow TypeScript best practices with import type for type-only imports, correct path aliases, and proper logger initialization at the module level as specified in the coding guidelines.


197-202: Well-structured interface definition.

Clear and properly typed interface that effectively defines the return structure for email fetching operations.


308-339: Clean implementation with good source organization.

The function effectively handles multiple email sources with proper error isolation. The configuration-driven approach makes it easy to maintain and extend.


341-355: Simple and effective implementation.

Good error handling with graceful degradation, returning an empty array instead of throwing when sent emails can't be fetched.

Comment on lines 21 to 195
async function fetchEmailsByQuery(
gmail: gmail_v1.Gmail,
query: string,
count: number,
): Promise<ParsedMessage[]> {
const emails: ParsedMessage[] = [];
let nextPageToken: string | undefined;
let retryCount = 0;
const maxRetries = 3;

logger.info("fetchEmailsByQuery started", {
query,
targetCount: count,
maxRetries,
});

while (emails.length < count && retryCount < maxRetries) {
try {
logger.info("fetchEmailsByQuery: calling getMessages", {
query,
maxResults: Math.min(100, count - emails.length),
hasPageToken: !!nextPageToken,
currentEmailsCount: emails.length,
retryCount,
});

const response = await getMessages(gmail, {
query: query || undefined,
maxResults: Math.min(100, count - emails.length),
pageToken: nextPageToken,
});

logger.info("fetchEmailsByQuery: getMessages response received", {
hasMessages: !!response.messages,
messagesCount: response.messages?.length || 0,
hasNextPageToken: !!response.nextPageToken,
responseKeys: Object.keys(response),
});

if (!response.messages || response.messages.length === 0) {
logger.info("fetchEmailsByQuery: no messages found, breaking");
break;
}

logger.info("fetchEmailsByQuery: starting to fetch individual messages", {
messageIdsCount: response.messages?.length || 0,
messageIds: response.messages?.map((m: any) => m.id).slice(0, 5) || [],
});

const messagePromises = (response.messages || []).map(
async (message: any, index: number) => {
if (!message.id) {
logger.warn("fetchEmailsByQuery: message without ID", {
index,
message,
});
return null;
}

logger.info("fetchEmailsByQuery: fetching individual message", {
messageId: message.id,
index,
totalMessages: response.messages?.length || 0,
});

for (let i = 0; i < 3; i++) {
try {
logger.info("fetchEmailsByQuery: calling getMessage", {
messageId: message.id,
attempt: i + 1,
format: "full",
});

const messageWithPayload = await getMessage(
message.id,
gmail,
"full",
);

logger.info("fetchEmailsByQuery: getMessage successful", {
messageId: message.id,
hasPayload: !!messageWithPayload,
payloadKeys: messageWithPayload
? Object.keys(messageWithPayload)
: [],
});

const parsedMessage = parseMessage(messageWithPayload);
logger.info("fetchEmailsByQuery: message parsed successfully", {
messageId: message.id,
hasHeaders: !!parsedMessage.headers,
hasTextPlain: !!parsedMessage.textPlain,
hasTextHtml: !!parsedMessage.textHtml,
});

return parsedMessage;
} catch (error) {
logger.warn("fetchEmailsByQuery: getMessage attempt failed", {
error,
messageId: message.id,
attempt: i + 1,
});

if (i === 2) {
logger.warn(
`Failed to fetch message ${message.id} after 3 attempts:`,
{ error },
);
return null;
}
await new Promise((resolve) =>
setTimeout(resolve, 1000 * (i + 1)),
);
}
}
return null;
},
);

logger.info("fetchEmailsByQuery: waiting for all message promises", {
promisesCount: messagePromises.length,
});

const messages = await Promise.all(messagePromises);
const validMessages = messages.filter((msg) => msg !== null);

logger.info("fetchEmailsByQuery: message promises completed", {
totalMessages: messages.length,
validMessages: validMessages.length,
nullMessages: messages.length - validMessages.length,
});

emails.push(...validMessages);

nextPageToken = response.nextPageToken || undefined;
if (!nextPageToken) {
logger.info("fetchEmailsByQuery: no next page token, breaking");
break;
}

retryCount = 0;
logger.info("fetchEmailsByQuery: successful iteration completed", {
currentEmailsCount: emails.length,
targetCount: count,
hasNextPageToken: !!nextPageToken,
});
} catch (error) {
retryCount++;
logger.error("fetchEmailsByQuery: main loop error", {
error,
retryCount,
maxRetries,
currentEmailsCount: emails.length,
targetCount: count,
});

if (retryCount >= maxRetries) {
logger.error(`Failed to fetch emails after ${maxRetries} attempts:`, {
error,
});
break;
}

await new Promise((resolve) => setTimeout(resolve, 2000 * retryCount));
}
}

logger.info("fetchEmailsByQuery completed", {
finalEmailsCount: emails.length,
targetCount: count,
finalRetryCount: retryCount,
});

return emails;
}
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

Refactor this large function into smaller, focused functions.

This function is quite complex and handles multiple responsibilities (pagination, retry logic, message fetching, parsing). Consider breaking it down into smaller functions for better maintainability and testability.

Consider extracting these responsibilities:

+// Extract constants
+const MAX_RESULTS_PER_PAGE = 100;
+const MAX_MESSAGE_RETRIES = 3;
+const RETRY_BASE_DELAY = 1000;
+const MAIN_RETRY_BASE_DELAY = 2000;
+
+async function fetchMessageWithRetry(messageId: string, gmail: gmail_v1.Gmail): Promise<ParsedMessage | null> {
+  for (let i = 0; i < MAX_MESSAGE_RETRIES; i++) {
+    try {
+      const messageWithPayload = await getMessage(messageId, gmail, "full");
+      return parseMessage(messageWithPayload);
+    } catch (error) {
+      logger.warn("getMessage attempt failed", { error, messageId, attempt: i + 1 });
+      if (i === MAX_MESSAGE_RETRIES - 1) return null;
+      await new Promise(resolve => setTimeout(resolve, RETRY_BASE_DELAY * (i + 1)));
+    }
+  }
+  return null;
+}
+
+async function fetchMessagesPage(gmail: gmail_v1.Gmail, query: string, maxResults: number, pageToken?: string) {
+  const response = await getMessages(gmail, {
+    query: query || undefined,
+    maxResults,
+    pageToken,
+  });
+  
+  if (!response.messages?.length) return { messages: [], nextPageToken: undefined };
+  
+  const messagePromises = response.messages.map(message => 
+    message.id ? fetchMessageWithRetry(message.id, gmail) : Promise.resolve(null)
+  );
+  
+  const messages = await Promise.all(messagePromises);
+  return {
+    messages: messages.filter(msg => msg !== null),
+    nextPageToken: response.nextPageToken
+  };
+}

This would make the main function much more readable and each piece easier to test.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In apps/web/app/(reports)/api/email-report/fetch.ts between lines 21 and 195,
the fetchEmailsByQuery function is too large and handles multiple
responsibilities like pagination, retry logic, fetching individual messages, and
parsing. Refactor by extracting smaller focused functions for each
responsibility: one for handling pagination and retry logic, one for fetching
and retrying individual messages, and one for parsing messages. Then, have the
main function orchestrate these smaller functions to improve readability and
maintainability.

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

🔭 Outside diff range comments (1)
.cursor/rules/llm.mdc (1)

50-82: Fix incomplete parameter renaming

The parameter has been renamed from user to emailAccount, but the destructuring on line 52 and the reference on line 81 haven't been updated. This will cause runtime errors.

export async function featureFunction(options: {
  inputData: InputType;
  emailAccount: EmailAccountWithAI;
}) {
-  const { inputData, user } = options;
+  const { inputData, emailAccount } = options;

  // ... other code ...

  const result = await chatCompletionObject({
    userAi: emailAccount.user,
    system,
    prompt,
    schema,
-    userEmail: user.email,
+    userEmail: emailAccount.email,
    usageLabel: "Feature Name",
  });
🧹 Nitpick comments (2)
apps/web/utils/ai/report/fetch.ts (1)

11-20: Fix typos in documentation

The documentation contains several typos that should be corrected for clarity.

- * Uses sequential message fetching instead of batch loads to avoid Gmail API rate limits.
- * This approach fetches one message at a time with retry and backofflogic, which is slower but more
- * reliable than trying to fetch 100 messages at once.
- *
- * Not usinggetMessagesLargeBatch because it expects the messageIds
- * queryBatchMessages is limited to 20 messages at a time
+ * Uses sequential message fetching instead of batch loads to avoid Gmail API rate limits.
+ * This approach fetches one message at a time with retry and backoff logic, which is slower but more
+ * reliable than trying to fetch 100 messages at once.
+ *
+ * Not using getMessagesLargeBatch because it expects the messageIds
+ * queryBatchMessages is limited to 20 messages at a time
apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx (1)

195-203: Remove duplicate "Professional Identity" heading

The card already has "Professional Identity" as its title, so the h4 with the same text is redundant.

                  <CardHeader>
                    <CardTitle className="flex items-center gap-2">
                      <TrendingUp className="h-5 w-5" />
                      Professional Identity
                    </CardTitle>
                  </CardHeader>
                  <CardContent className="space-y-6">
                    <div>
-                      <h4 className="font-semibold text-gray-900 mb-3">
-                        Professional Identity
-                      </h4>
                      <p className="text-lg font-medium text-blue-600 mb-2">
                        {report.userPersona.professionalIdentity.persona}
                      </p>
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a33bc1e and a5ac182.

📒 Files selected for processing (8)
  • .cursor/rules/llm.mdc (2 hunks)
  • apps/web/app/(app)/[emailAccountId]/debug/page.tsx (1 hunks)
  • apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx (1 hunks)
  • apps/web/app/(landing)/components/test-action.ts (1 hunks)
  • apps/web/utils/actions/report.ts (1 hunks)
  • apps/web/utils/ai/report/fetch.ts (1 hunks)
  • apps/web/utils/ai/report/prompts.ts (1 hunks)
  • apps/web/utils/gmail/message.ts (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • apps/web/utils/gmail/message.ts
  • apps/web/app/(landing)/components/test-action.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/app/(app)/[emailAccountId]/debug/page.tsx
🧰 Additional context used
📓 Path-based instructions (22)
.cursor/rules/*.mdc

📄 CodeRabbit Inference Engine (.cursor/rules/cursor-rules.mdc)

.cursor/rules/*.mdc: Always place rule files in PROJECT_ROOT/.cursor/rules/
Follow the naming convention: use kebab-case for filenames, always use .mdc extension, and make names descriptive of the rule's purpose
Cursor rules must have a frontmatter section with description, globs, and alwaysApply fields, followed by markdown content explaining the rule

Files:

  • .cursor/rules/llm.mdc
!{.cursor/rules/*.mdc}

📄 CodeRabbit Inference Engine (.cursor/rules/cursor-rules.mdc)

Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location

Files:

  • .cursor/rules/llm.mdc
  • apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
  • apps/web/utils/actions/report.ts
  • apps/web/utils/ai/report/fetch.ts
  • apps/web/utils/ai/report/prompts.ts
!pages/_document.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)

!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.

Files:

  • .cursor/rules/llm.mdc
  • apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
  • apps/web/utils/actions/report.ts
  • apps/web/utils/ai/report/fetch.ts
  • apps/web/utils/ai/report/prompts.ts
apps/web/**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use @/ for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX

Files:

  • apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
  • apps/web/utils/actions/report.ts
  • apps/web/utils/ai/report/fetch.ts
  • apps/web/utils/ai/report/prompts.ts
apps/web/app/**

📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)

NextJS app router structure with (app) directory

Files:

  • apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
apps/web/**/*.tsx

📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)

apps/web/**/*.tsx: Follow tailwindcss patterns with prettier-plugin-tailwindcss
Prefer functional components with hooks
Use shadcn/ui components when available
Ensure responsive design with mobile-first approach
Follow consistent naming conventions (PascalCase for components)
Use LoadingContent component for async data
Use result?.serverError with toastError and toastSuccess
Use LoadingContent component to handle loading and error states consistently
Pass loading, error, and children props to LoadingContent

Files:

  • apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
**/*.tsx

📄 CodeRabbit Inference Engine (.cursor/rules/form-handling.mdc)

**/*.tsx: Use React Hook Form with Zod for validation
Validate form inputs before submission
Show validation errors inline next to form fields

Files:

  • apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/logging.mdc)

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

Import Prisma in the project using import prisma from "@/utils/prisma";

**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.

Files:

  • apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
  • apps/web/utils/actions/report.ts
  • apps/web/utils/ai/report/fetch.ts
  • apps/web/utils/ai/report/prompts.ts
apps/web/app/(app)/*/**

📄 CodeRabbit Inference Engine (.cursor/rules/page-structure.mdc)

Components for the page are either put in page.tsx, or in the apps/web/app/(app)/PAGE_NAME folder

Files:

  • apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
apps/web/app/(app)/*/**/*.tsx

📄 CodeRabbit Inference Engine (.cursor/rules/page-structure.mdc)

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]/debug/report/page.tsx
apps/web/app/(app)/*/**/**/*.tsx

📄 CodeRabbit Inference Engine (.cursor/rules/page-structure.mdc)

If we're in a deeply nested component we will use swr to fetch via API

Files:

  • apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
apps/web/app/**/*.tsx

📄 CodeRabbit Inference Engine (.cursor/rules/project-structure.mdc)

Components with onClick must be client components with use client directive

Files:

  • apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)

**/*.{js,jsx,ts,tsx}: Don't use elements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...

Files:

  • apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
  • apps/web/utils/actions/report.ts
  • apps/web/utils/ai/report/fetch.ts
  • apps/web/utils/ai/report/prompts.ts
**/*.{jsx,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)

**/*.{jsx,tsx}: Don't destructure props inside JSX components in Solid projects.
Don't use both children and dangerouslySetInnerHTML props on the same element.
Don't use Array index in keys.
Don't assign to React component props.
Don't define React components inside other components.
Don't use event handlers on non-interactive elements.
Don't assign JSX properties multiple times.
Don't add extra closing tags for components without children.
Use <>...</> instead of ....
Don't insert comments as text nodes.
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 use unnecessary fragments.
Don't pass children as props.
Use semantic elements instead of role attributes in JSX.

Files:

  • apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
**/*.{html,jsx,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)

**/*.{html,jsx,tsx}: Don't use or elements.
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.
Only use the scope prop on 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.
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 ARIA state and property values.
Use valid values for the autocomplete attribute on input eleme...

Files:

  • apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
apps/web/utils/actions/**/*.ts

📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)

apps/web/utils/actions/**/*.ts: Use server actions for all mutations (create/update/delete operations)
next-safe-action provides centralized error handling
Use Zod schemas for validation on both client and server
Use revalidatePath in server actions for cache invalidation

apps/web/utils/actions/**/*.ts: Use server actions (with next-safe-action) for all mutations (create/update/delete operations); do NOT use POST API routes for mutations.
Use revalidatePath in server actions to invalidate cache after mutations.

Files:

  • apps/web/utils/actions/report.ts
**/*.ts

📄 CodeRabbit Inference Engine (.cursor/rules/form-handling.mdc)

**/*.ts: The same validation should be done in the server action too
Define validation schemas using Zod

Files:

  • apps/web/utils/actions/report.ts
  • apps/web/utils/ai/report/fetch.ts
  • apps/web/utils/ai/report/prompts.ts
apps/web/utils/actions/*.ts

📄 CodeRabbit Inference Engine (.cursor/rules/server-actions.mdc)

apps/web/utils/actions/*.ts: Implement all server actions using the next-safe-action library for type safety, input validation, context management, and error handling. Refer to apps/web/utils/actions/safe-action.ts for client definitions (actionClient, actionClientUser, adminActionClient).
Use actionClientUser when only authenticated user context (userId) is needed.
Use actionClient when both authenticated user context and a specific emailAccountId are needed. The emailAccountId must be bound when calling the action from the client.
Use adminActionClient for actions restricted to admin users.
Access necessary context (like userId, emailAccountId, etc.) provided by the safe action client via the ctx object in the .action() handler.
Server Actions are strictly for mutations (operations that change data, e.g., creating, updating, deleting). Do NOT use Server Actions for data fetching (GET operations). For data fetching, use dedicated GET API Routes combined with SWR Hooks.
Use SafeError for expected/handled errors within actions if needed. next-safe-action provides centralized error handling.
Use the .metadata({ name: "actionName" }) method to provide a meaningful name for monitoring. Sentry instrumentation is automatically applied via withServerActionInstrumentation within the safe action clients.
If an action modifies data displayed elsewhere, use revalidatePath or revalidateTag from next/cache within the action handler as needed.

Server action files must start with use server

Files:

  • apps/web/utils/actions/report.ts
apps/web/utils/**

📄 CodeRabbit Inference Engine (.cursor/rules/project-structure.mdc)

Create utility functions in utils/ folder for reusable logic

Files:

  • apps/web/utils/actions/report.ts
  • apps/web/utils/ai/report/fetch.ts
  • apps/web/utils/ai/report/prompts.ts
apps/web/utils/**/*.ts

📄 CodeRabbit Inference Engine (.cursor/rules/project-structure.mdc)

apps/web/utils/**/*.ts: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size

Files:

  • apps/web/utils/actions/report.ts
  • apps/web/utils/ai/report/fetch.ts
  • apps/web/utils/ai/report/prompts.ts
apps/web/utils/{ai,llms}/**/*

📄 CodeRabbit Inference Engine (.cursor/rules/llm.mdc)

apps/web/utils/{ai,llms}/**/*: LLM-related code must be organized in the directories: apps/web/utils/ai/, apps/web/utils/llms/, and apps/web/tests/ for LLM-specific tests.
Keep related AI functions in the same file or directory.

Files:

  • apps/web/utils/ai/report/fetch.ts
  • apps/web/utils/ai/report/prompts.ts
apps/web/utils/{ai,llms}/**/*.ts

📄 CodeRabbit Inference Engine (.cursor/rules/llm.mdc)

apps/web/utils/{ai,llms}/**/*.ts: Follow the standard structure for LLM-related functions: use a scoped logger, define a Zod schema for output, validate inputs early, separate system and user prompts, log inputs and outputs, call chatCompletionObject with proper configuration, and return validated results.
Keep system prompts and user prompts separate in LLM-related code.
System prompt should define the LLM's role and task specifications.
User prompt should contain the actual data and context.
Always define a Zod schema for response validation in LLM-related functions.
Make Zod 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 in LLM-related functions.
Include relevant context in log messages for LLM-related code.
Implement early returns for invalid inputs in LLM-related functions.
Use proper error types and logging in LLM-related code.
Implement fallbacks for AI failures in LLM-related functions.
Add retry logic for transient failures using withRetry in LLM-related code.
Use XML-like tags to structure data in LLM prompts.
Remove excessive whitespace and truncate long inputs in LLM prompts.
Format data consistently across similar LLM-related functions.
Use TypeScript types for all parameters and return values in LLM-related code.
Define clear interfaces for complex input/output structures in LLM-related code.
Extract common patterns into utility functions in LLM-related code.
Document complex AI logic with clear comments in LLM-related code.

Files:

  • apps/web/utils/ai/report/fetch.ts
  • apps/web/utils/ai/report/prompts.ts
🧠 Learnings (6)
📓 Common learnings
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/index.mdc:0-0
Timestamp: 2025-07-18T15:05:56.644Z
Learning: Guidelines for working with Gmail API
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-07-19T17:50:28.270Z
Learning: The `utils` folder also contains core app logic such as Next.js Server Actions and Gmail API requests.
.cursor/rules/llm.mdc (16)

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Follow the standard structure for LLM-related functions: use a scoped logger, define a Zod schema for output, validate inputs early, separate system and user prompts, log inputs and outputs, call chatCompletionObject with proper configuration, and return validated results.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-07-18T17:27:58.249Z
Learning: Applies to apps/web/utils/actions/*.ts : Use actionClient when both authenticated user context and a specific emailAccountId are needed. The emailAccountId must be bound when calling the action from the client.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Keep system prompts and user prompts separate in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/app/api/**/route.ts : Use withEmailAccount for email-account-level operations

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Implement fallbacks for AI failures in LLM-related functions.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Format data consistently across similar LLM-related functions.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use TypeScript types for all parameters and return values in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : System prompt should define the LLM's role and task specifications.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : User prompt should contain the actual data and context.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use proper error types and logging in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-07-18T15:06:10.570Z
Learning: Run AI tests with: pnpm test-ai your-feature

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/posthog-feature-flags.mdc:0-0
Timestamp: 2025-07-18T15:07:12.415Z
Learning: Applies to apps/web/hooks/useFeatureFlags.ts : Use kebab-case for feature flag keys (e.g., 'inbox-cleaner', 'pricing-options-2')

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Include relevant context in log messages for LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Log inputs and outputs with appropriate log levels in LLM-related functions.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Document complex AI logic with clear comments in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Remove excessive whitespace and truncate long inputs in LLM prompts.

apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx (23)

Learnt from: elie222
PR: #537
File: apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx:30-34
Timestamp: 2025-07-08T13:14:07.449Z
Learning: The clean onboarding page in apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx is intentionally Gmail-specific and should show an error for non-Google email accounts rather than attempting to support multiple providers.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-07-18T17:27:58.249Z
Learning: Applies to apps/web/utils/actions/*.ts : Use actionClient when both authenticated user context and a specific emailAccountId are needed. The emailAccountId must be bound when calling the action from the client.

Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/app/api/**/route.ts : Use withEmailAccount for email-account-level operations

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-07-20T09:00:16.505Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Create new pages at: apps/web/app/(app)/PAGE_NAME/page.tsx

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-07-18T15:07:00.269Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Create new pages at: apps/web/app/(app)/PAGE_NAME/page.tsx

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-07-20T09:00:16.505Z
Learning: Applies to apps/web/app/(app)/*/page.tsx : Pages are Server components for direct data loading

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-07-20T09:00:16.505Z
Learning: Applies to apps/web/app/(app)/*/ : Components for the page are either in page.tsx or in the apps/web/app/(app)/PAGE_NAME folder

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-07-18T17:27:46.389Z
Learning: Applies to /api//route.ts : Use withEmailAccount middleware for API routes that operate on a specific email account (i.e., use or require emailAccountId).

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/page-structure.mdc:0-0
Timestamp: 2025-07-18T15:07:00.269Z
Learning: Applies to apps/web/app/(app)/*/** : Components for the page are either put in page.tsx, or in the apps/web/app/(app)/PAGE_NAME folder

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-07-18T15:06:10.570Z
Learning: Applies to apps/web/tests/**/*.test.ts : Use descriptive console.debug statements for generated content in LLM-related tests

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use proper error types and logging in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/**/*.{ts,tsx} : Use proper error handling with try/catch blocks

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-07-20T09:00:41.968Z
Learning: Applies to apps/web/app/api/**/*.{ts,js} : Error messages in API routes must not reveal internal details; use generic errors and SafeError for user-facing errors.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Follow the standard structure for LLM-related functions: use a scoped logger, define a Zod schema for output, validate inputs early, separate system and user prompts, log inputs and outputs, call chatCompletionObject with proper configuration, and return validated results.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : User prompt should contain the actual data and context.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Include relevant context in log messages for LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-20T09:03:06.318Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Make sure to pass a message value when creating a built-in error.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use descriptive scoped loggers for each LLM feature.

Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/**/*.tsx : Use LoadingContent component to handle loading and error states consistently

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/index.mdc:0-0
Timestamp: 2025-07-18T15:05:56.644Z
Learning: UI component and styling guidelines (Shadcn, Tailwind)

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.146Z
Learning: Applies to apps/web/components/**/*.tsx : Use the LoadingContent component to handle loading and error states consistently in data-fetching components.

Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/**/*.tsx : Use LoadingContent component for async data

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-07-19T17:50:22.078Z
Learning: Applies to components/**/*.tsx : Implement responsive design with Tailwind CSS using a mobile-first approach

apps/web/utils/actions/report.ts (10)

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-07-18T17:27:58.249Z
Learning: Applies to apps/web/utils/actions/*.ts : Use actionClient when both authenticated user context and a specific emailAccountId are needed. The emailAccountId must be bound when calling the action from the client.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-07-18T17:27:58.249Z
Learning: Applies to apps/web/utils/actions/*.ts : Implement all server actions using the next-safe-action library for type safety, input validation, context management, and error handling. Refer to apps/web/utils/actions/safe-action.ts for client definitions (actionClient, actionClientUser, adminActionClient).

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-07-18T15:05:34.899Z
Learning: Applies to apps/web/utils/gmail/**/*.ts : Keep provider-specific implementation details isolated in the appropriate utils subfolder (e.g., 'apps/web/utils/gmail/')

Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/utils/actions/**/*.ts : Use server actions for all mutations (create/update/delete operations)

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use proper error types and logging in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-07-19T17:50:28.270Z
Learning: The utils folder also contains core app logic such as Next.js Server Actions and Gmail API requests.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-07-18T17:27:58.249Z
Learning: Applies to apps/web/utils/actions/*.ts : Use the .metadata({ name: "actionName" }) method to provide a meaningful name for monitoring. Sentry instrumentation is automatically applied via withServerActionInstrumentation within the safe action clients.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-07-20T09:00:16.505Z
Learning: Applies to apps/web/utils/actions/*.ts : Server action files must start with use server

Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/app/api/**/route.ts : Use withEmailAccount for email-account-level operations

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use TypeScript types for all parameters and return values in LLM-related code.

apps/web/utils/ai/report/fetch.ts (9)

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-07-18T15:05:34.899Z
Learning: Applies to apps/web/utils/gmail/**/*.ts : Keep provider-specific implementation details isolated in the appropriate utils subfolder (e.g., 'apps/web/utils/gmail/')

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Implement fallbacks for AI failures in LLM-related functions.

Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/app/api/**/route.ts : Use withEmailAccount for email-account-level operations

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Extract common patterns into utility functions in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-07-19T17:50:28.270Z
Learning: The utils folder also contains core app logic such as Next.js Server Actions and Gmail API requests.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-07-18T17:27:46.389Z
Learning: Applies to /api//route.ts : Use withEmailAccount middleware for API routes that operate on a specific email account (i.e., use or require emailAccountId).

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use proper error types and logging in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/index.mdc:0-0
Timestamp: 2025-07-18T15:05:56.644Z
Learning: Guidelines for working with Gmail API

Learnt from: elie222
PR: #537
File: apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx:30-34
Timestamp: 2025-07-08T13:14:07.449Z
Learning: The clean onboarding page in apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx is intentionally Gmail-specific and should show an error for non-Google email accounts rather than attempting to support multiple providers.

apps/web/utils/ai/report/prompts.ts (10)

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Follow the standard structure for LLM-related functions: use a scoped logger, define a Zod schema for output, validate inputs early, separate system and user prompts, log inputs and outputs, call chatCompletionObject with proper configuration, and return validated results.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Keep system prompts and user prompts separate in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : System prompt should define the LLM's role and task specifications.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Document complex AI logic with clear comments in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Implement fallbacks for AI failures in LLM-related functions.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Make Zod schemas as specific as possible to guide the LLM output.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Extract common patterns into utility functions in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use XML-like tags to structure data in LLM prompts.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : User prompt should contain the actual data and context.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Define clear interfaces for complex input/output structures in LLM-related code.

🧬 Code Graph Analysis (1)
apps/web/utils/ai/report/fetch.ts (5)
apps/web/utils/logger.ts (1)
  • createScopedLogger (17-65)
apps/web/utils/types.ts (1)
  • ParsedMessage (55-70)
apps/web/utils/gmail/message.ts (3)
  • getMessages (252-270)
  • getMessage (66-78)
  • parseMessage (22-31)
apps/web/utils/llms/types.ts (1)
  • EmailAccountWithAI (10-24)
apps/web/utils/account.ts (1)
  • getGmailClientForEmail (14-27)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Static Code Analysis Js
  • GitHub Check: Jit Security
🔇 Additional comments (19)
apps/web/utils/ai/report/fetch.ts (6)

1-10: LGTM!

Imports are properly organized and follow project conventions. The scoped logger is correctly initialized.


21-195: Well-implemented email fetching with robust error handling

The function has excellent retry logic, comprehensive logging, and handles Gmail API rate limits gracefully. The parallel fetching of messages within each page while maintaining sequential page fetching is a good balance between performance and reliability.


197-232: Clean implementation with proper rate limiting

The interface is well-defined and the function properly handles fetching both received and sent emails with appropriate delays to avoid rate limiting.


234-265: Smart email source prioritization

Good implementation that fetches from multiple sources (inbox, archived, trash) without duplicates. The error handling ensures resilience when one source fails.


267-281: LGTM!

Simple and effective implementation for fetching sent emails with proper error handling.


283-312: Robust template extraction with proper error handling

Good implementation that gracefully handles errors at both the individual draft and overall function levels. The template limit of 10 is reasonable.

apps/web/utils/actions/report.ts (4)

1-26: Excellent server action setup

The file correctly implements all server action requirements: "use server" directive, proper use of actionClient, and well-organized imports. The EmailReportData type export is helpful for client components.


27-32: LGTM!

The action correctly uses actionClient with emailAccountId from context, following the project's server action patterns.


34-96: Robust label fetching with excellent error handling

The function properly filters system labels, fetches detailed counts, and handles errors gracefully at multiple levels. The sorting by message count is a nice touch for report presentation.


98-133: LGTM!

Proper implementation for fetching Gmail signature with appropriate null checks and error handling.

apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx (2)

53-589: Excellent UI implementation

The component has a well-structured, comprehensive UI that displays all report sections with proper visual hierarchy, responsive layouts, and appropriate use of badges for categorization. Good use of LoadingContent for state management.


591-622: LGTM!

Clean helper functions with intuitive color coding for badges. The exhaustive switch statements ensure all cases are handled.

apps/web/utils/ai/report/prompts.ts (7)

1-164: Excellent schema definitions following LLM best practices

The schemas are comprehensive, well-documented with describe() calls, and specific enough to guide LLM output effectively. The setup follows all the recommended patterns.


166-264: Excellent implementation following LLM guidelines

The function perfectly follows the LLM pattern with separated prompts, proper logging, and schema validation. The detailed persona examples and identification instructions will help generate accurate professional personas.


266-323: LGTM!

Good implementation with proper handling of optional data and clear prompt structure.


325-423: Excellent prompt engineering with clear category guidelines

Both functions follow LLM patterns correctly. The detailed instructions in analyzeResponsePatterns about avoiding meaningless categories (like "Other", "Miscellaneous") and providing good category examples is excellent prompt engineering.


425-502: LGTM!

Both functions properly implement the LLM pattern with context-aware prompts that will generate practical, actionable recommendations.


504-563: Smart batching implementation

Excellent implementation with batching to handle large email volumes and delays between batches to avoid rate limits. Comprehensive logging helps with debugging.


565-661: Robust batch processing with excellent logging

The function handles email batches efficiently with content truncation, detailed logging at each step, and clear summarization rules. The logging of AI provider details is particularly helpful for debugging.

Comment on lines +33 to +51
const { executeAsync, isExecuting, result } = useAction(
generateReportAction.bind(null, emailAccountId),
{
onSuccess: () => {
if (result?.data) {
setReport(result.data);
toastSuccess({ description: "Report generated successfully" });
} else {
toastError({ description: "Failed to generate report" });
}
},
onError: (result) => {
toastError({
title: "Failed to generate report",
description: result.error.serverError || "Unknown error",
});
},
},
);
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

Fix result access in onSuccess handler

The onSuccess handler is accessing result from the outer scope instead of using the callback parameter. This could lead to stale data.

  const { executeAsync, isExecuting, result } = useAction(
    generateReportAction.bind(null, emailAccountId),
    {
-      onSuccess: () => {
-        if (result?.data) {
-          setReport(result.data);
+      onSuccess: (result) => {
+        if (result.data) {
+          setReport(result.data);
          toastSuccess({ description: "Report generated successfully" });
        } else {
          toastError({ description: "Failed to generate report" });
        }
      },
      onError: (result) => {
        toastError({
          title: "Failed to generate report",
          description: result.error.serverError || "Unknown error",
        });
      },
    },
  );
📝 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
const { executeAsync, isExecuting, result } = useAction(
generateReportAction.bind(null, emailAccountId),
{
onSuccess: () => {
if (result?.data) {
setReport(result.data);
toastSuccess({ description: "Report generated successfully" });
} else {
toastError({ description: "Failed to generate report" });
}
},
onError: (result) => {
toastError({
title: "Failed to generate report",
description: result.error.serverError || "Unknown error",
});
},
},
);
const { executeAsync, isExecuting, result } = useAction(
generateReportAction.bind(null, emailAccountId),
{
onSuccess: (result) => {
if (result.data) {
setReport(result.data);
toastSuccess({ description: "Report generated successfully" });
} else {
toastError({ description: "Failed to generate report" });
}
},
onError: (result) => {
toastError({
title: "Failed to generate report",
description: result.error.serverError || "Unknown error",
});
},
},
);
🤖 Prompt for AI Agents
In apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx around lines 33 to
51, the onSuccess handler incorrectly accesses the outer scoped result variable
instead of using the result parameter passed to the callback. To fix this,
update the onSuccess handler to accept a parameter (e.g., response) and use this
parameter to access the data instead of the outer result variable, ensuring
fresh and accurate data is used.

Comment on lines 204 to 212
executiveSummary,
emailActivityOverview: {
dataSources: {
inbox: totalReceived,
archived: 0,
trash: 0,
sent: totalSent,
},
},
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

Update data sources to reflect actual email fetching

The dataSources object shows 0 for archived and trash, but fetchReceivedEmails actually fetches from these sources. Consider tracking the actual counts from each source for accuracy.

The current implementation in fetchReceivedEmails fetches from inbox, archived, and trash but doesn't return counts per source. You might want to refactor fetchEmailsForReport and fetchReceivedEmails to return source-specific counts for accurate reporting.

🤖 Prompt for AI Agents
In apps/web/utils/actions/report.ts around lines 204 to 212, the dataSources
object sets archived and trash counts to zero, which is inaccurate since
fetchReceivedEmails fetches emails from these sources. Refactor
fetchReceivedEmails and fetchEmailsForReport to return counts of emails fetched
from each source (inbox, archived, trash) separately, then update the
dataSources object to use these actual counts instead of zeros for archived and
trash to ensure accurate reporting.

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

♻️ Duplicate comments (1)
apps/web/utils/actions/report.ts (1)

225-234: Update data sources to reflect actual email fetching

The dataSources object shows 0 for archived and trash, but fetchReceivedEmails actually fetches from these sources. Consider tracking the actual counts from each source for accuracy.

🧹 Nitpick comments (1)
apps/web/utils/ai/report/prompts.ts (1)

544-586: Consider adding error handling for individual batch failures.

While the function follows LLM best practices, consider adding error handling around the chatCompletionObject call to prevent entire batch failures from stopping the summarization process.

  const result = await chatCompletionObject({
    userAi: emailAccount.user,
    system,
    prompt,
    schema: z.array(emailSummarySchema),
    userEmail: emailAccount.email,
    usageLabel: "email-report-summary-generation",
-  });
+  }).catch((error) => {
+    logger.error("Failed to process email batch", { 
+      batchNumber, 
+      totalBatches, 
+      error: error instanceof Error ? error.message : String(error) 
+    });
+    // Return empty array or partial results based on your needs
+    return { object: [] };
+  });
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0bd0291 and e250fca.

📒 Files selected for processing (6)
  • apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx (1 hunks)
  • apps/web/utils/actions/report.ts (1 hunks)
  • apps/web/utils/ai/report/fetch.ts (1 hunks)
  • apps/web/utils/ai/report/prompts.ts (1 hunks)
  • apps/web/utils/get-email-from-message.ts (1 hunks)
  • apps/web/utils/types.ts (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • apps/web/utils/get-email-from-message.ts
  • apps/web/utils/types.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/web/utils/ai/report/fetch.ts
  • apps/web/app/(app)/[emailAccountId]/debug/report/page.tsx
🧰 Additional context used
📓 Path-based instructions (12)
apps/web/**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use @/ for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX

Files:

  • apps/web/utils/actions/report.ts
  • apps/web/utils/ai/report/prompts.ts
apps/web/utils/actions/**/*.ts

📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)

apps/web/utils/actions/**/*.ts: Use server actions for all mutations (create/update/delete operations)
next-safe-action provides centralized error handling
Use Zod schemas for validation on both client and server
Use revalidatePath in server actions for cache invalidation

apps/web/utils/actions/**/*.ts: Use server actions (with next-safe-action) for all mutations (create/update/delete operations); do NOT use POST API routes for mutations.
Use revalidatePath in server actions to invalidate cache after mutations.

Files:

  • apps/web/utils/actions/report.ts
!{.cursor/rules/*.mdc}

📄 CodeRabbit Inference Engine (.cursor/rules/cursor-rules.mdc)

Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location

Files:

  • apps/web/utils/actions/report.ts
  • apps/web/utils/ai/report/prompts.ts
**/*.ts

📄 CodeRabbit Inference Engine (.cursor/rules/form-handling.mdc)

**/*.ts: The same validation should be done in the server action too
Define validation schemas using Zod

Files:

  • apps/web/utils/actions/report.ts
  • apps/web/utils/ai/report/prompts.ts
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/logging.mdc)

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

Import Prisma in the project using import prisma from "@/utils/prisma";

**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.

Files:

  • apps/web/utils/actions/report.ts
  • apps/web/utils/ai/report/prompts.ts
apps/web/utils/actions/*.ts

📄 CodeRabbit Inference Engine (.cursor/rules/server-actions.mdc)

apps/web/utils/actions/*.ts: Implement all server actions using the next-safe-action library for type safety, input validation, context management, and error handling. Refer to apps/web/utils/actions/safe-action.ts for client definitions (actionClient, actionClientUser, adminActionClient).
Use actionClientUser when only authenticated user context (userId) is needed.
Use actionClient when both authenticated user context and a specific emailAccountId are needed. The emailAccountId must be bound when calling the action from the client.
Use adminActionClient for actions restricted to admin users.
Access necessary context (like userId, emailAccountId, etc.) provided by the safe action client via the ctx object in the .action() handler.
Server Actions are strictly for mutations (operations that change data, e.g., creating, updating, deleting). Do NOT use Server Actions for data fetching (GET operations). For data fetching, use dedicated GET API Routes combined with SWR Hooks.
Use SafeError for expected/handled errors within actions if needed. next-safe-action provides centralized error handling.
Use the .metadata({ name: "actionName" }) method to provide a meaningful name for monitoring. Sentry instrumentation is automatically applied via withServerActionInstrumentation within the safe action clients.
If an action modifies data displayed elsewhere, use revalidatePath or revalidateTag from next/cache within the action handler as needed.

Server action files must start with use server

Files:

  • apps/web/utils/actions/report.ts
apps/web/utils/**

📄 CodeRabbit Inference Engine (.cursor/rules/project-structure.mdc)

Create utility functions in utils/ folder for reusable logic

Files:

  • apps/web/utils/actions/report.ts
  • apps/web/utils/ai/report/prompts.ts
apps/web/utils/**/*.ts

📄 CodeRabbit Inference Engine (.cursor/rules/project-structure.mdc)

apps/web/utils/**/*.ts: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size

Files:

  • apps/web/utils/actions/report.ts
  • apps/web/utils/ai/report/prompts.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)

**/*.{js,jsx,ts,tsx}: Don't use elements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...

Files:

  • apps/web/utils/actions/report.ts
  • apps/web/utils/ai/report/prompts.ts
!pages/_document.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)

!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.

Files:

  • apps/web/utils/actions/report.ts
  • apps/web/utils/ai/report/prompts.ts
apps/web/utils/{ai,llms}/**/*

📄 CodeRabbit Inference Engine (.cursor/rules/llm.mdc)

apps/web/utils/{ai,llms}/**/*: LLM-related code must be organized in the directories: apps/web/utils/ai/, apps/web/utils/llms/, and apps/web/tests/ for LLM-specific tests.
Keep related AI functions in the same file or directory.

Files:

  • apps/web/utils/ai/report/prompts.ts
apps/web/utils/{ai,llms}/**/*.ts

📄 CodeRabbit Inference Engine (.cursor/rules/llm.mdc)

apps/web/utils/{ai,llms}/**/*.ts: Follow the standard structure for LLM-related functions: use a scoped logger, define a Zod schema for output, validate inputs early, separate system and user prompts, log inputs and outputs, call chatCompletionObject with proper configuration, and return validated results.
Keep system prompts and user prompts separate in LLM-related code.
System prompt should define the LLM's role and task specifications.
User prompt should contain the actual data and context.
Always define a Zod schema for response validation in LLM-related functions.
Make Zod 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 in LLM-related functions.
Include relevant context in log messages for LLM-related code.
Implement early returns for invalid inputs in LLM-related functions.
Use proper error types and logging in LLM-related code.
Implement fallbacks for AI failures in LLM-related functions.
Add retry logic for transient failures using withRetry in LLM-related code.
Use XML-like tags to structure data in LLM prompts.
Remove excessive whitespace and truncate long inputs in LLM prompts.
Format data consistently across similar LLM-related functions.
Use TypeScript types for all parameters and return values in LLM-related code.
Define clear interfaces for complex input/output structures in LLM-related code.
Extract common patterns into utility functions in LLM-related code.
Document complex AI logic with clear comments in LLM-related code.

Files:

  • apps/web/utils/ai/report/prompts.ts
🧠 Learnings (2)
apps/web/utils/actions/report.ts (16)

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-07-18T17:27:58.249Z
Learning: Applies to apps/web/utils/actions/*.ts : Use actionClient when both authenticated user context and a specific emailAccountId are needed. The emailAccountId must be bound when calling the action from the client.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-07-18T17:27:58.249Z
Learning: Applies to apps/web/utils/actions/*.ts : Implement all server actions using the next-safe-action library for type safety, input validation, context management, and error handling. Refer to apps/web/utils/actions/safe-action.ts for client definitions (actionClient, actionClientUser, adminActionClient).

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-07-18T15:05:34.899Z
Learning: Applies to apps/web/utils/gmail/**/*.ts : Keep provider-specific implementation details isolated in the appropriate utils subfolder (e.g., 'apps/web/utils/gmail/')

Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/utils/actions/**/*.ts : Use server actions for all mutations (create/update/delete operations)

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use proper error types and logging in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/app/api/**/route.ts : Use withEmailAccount for email-account-level operations

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-07-19T17:50:28.270Z
Learning: The utils folder also contains core app logic such as Next.js Server Actions and Gmail API requests.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-07-18T17:27:58.249Z
Learning: Applies to apps/web/utils/actions/*.ts : Use the .metadata({ name: "actionName" }) method to provide a meaningful name for monitoring. Sentry instrumentation is automatically applied via withServerActionInstrumentation within the safe action clients.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-07-20T09:00:16.505Z
Learning: Applies to apps/web/utils/actions/*.ts : Server action files must start with use server

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.146Z
Learning: Applies to apps/web/utils/actions/**/*.ts : Use server actions (with next-safe-action) for all mutations (create/update/delete operations); do NOT use POST API routes for mutations.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : User prompt should contain the actual data and context.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Format data consistently across similar LLM-related functions.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Implement fallbacks for AI failures in LLM-related functions.

Learnt from: elie222
PR: #537
File: apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx:30-34
Timestamp: 2025-07-08T13:14:07.449Z
Learning: The clean onboarding page in apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx is intentionally Gmail-specific and should show an error for non-Google email accounts rather than attempting to support multiple providers.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Implement early returns for invalid inputs in LLM-related functions.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Document complex AI logic with clear comments in LLM-related code.

apps/web/utils/ai/report/prompts.ts (10)

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Follow the standard structure for LLM-related functions: use a scoped logger, define a Zod schema for output, validate inputs early, separate system and user prompts, log inputs and outputs, call chatCompletionObject with proper configuration, and return validated results.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Keep system prompts and user prompts separate in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : System prompt should define the LLM's role and task specifications.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Document complex AI logic with clear comments in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Implement fallbacks for AI failures in LLM-related functions.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Extract common patterns into utility functions in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use XML-like tags to structure data in LLM prompts.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Make Zod schemas as specific as possible to guide the LLM output.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : User prompt should contain the actual data and context.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Define clear interfaces for complex input/output structures in LLM-related code.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Static Code Analysis Js
  • GitHub Check: Jit Security
🔇 Additional comments (13)
apps/web/utils/actions/report.ts (5)

28-33: LGTM! Server action follows best practices.

The action correctly uses actionClient for email account context, includes metadata for monitoring, and has proper schema validation. The intensive AI report generation justifies using a server action despite being primarily a data operation.


35-97: Excellent error handling and performance optimization.

The function demonstrates solid practices:

  • Concurrent fetching with Promise.all for performance
  • Proper error handling at both function and individual label levels
  • Type guards ensure data integrity
  • Graceful degradation with fallback values on API failures

99-134: Solid implementation with proper error handling.

The function correctly handles the Gmail sendAs API flow with appropriate fallbacks and informative logging.


170-213: Excellent concurrent processing with robust error handling.

The Promise.all implementation with individual .catch() handlers ensures that failures in one AI function don't break the entire report generation. This approach maximizes data availability while maintaining resilience.


215-223: Smart conditional logic for dependent recommendations.

The conditional generation of actionable recommendations based on successful user persona generation is a good design pattern that prevents unnecessary AI calls when prerequisites are missing.

apps/web/utils/ai/report/prompts.ts (8)

12-168: Excellent schema design for structured AI outputs.

The Zod schemas are comprehensive and well-designed:

  • Detailed descriptions guide LLM output effectively
  • Appropriate use of enums, constraints, and nested objects
  • Schemas are specific enough to ensure consistent AI responses

This follows LLM best practices for output validation and structure.


170-268: Exemplary LLM function structure and prompt engineering.

This function demonstrates excellent adherence to LLM best practices:

  • Clear separation of system prompt (role definition) and user prompt (data)
  • Comprehensive prompt engineering with good/bad examples
  • Proper use of chatCompletionObject with schema validation
  • Appropriate usage labeling for monitoring

The detailed persona identification logic with confidence scoring is particularly well-designed.


270-327: Clean implementation with good optional parameter handling.

The function properly handles optional parameters (sentEmailSummaries, gmailSignature, gmailTemplates) using conditional template strings, and maintains the standard LLM structure.


329-370: Standard LLM implementation with clear focus on actionable insights.

The function follows established patterns and maintains focus on automation opportunities and actionable patterns, which aligns well with the report's practical objectives.


372-427: Thoughtful prompt engineering prevents generic categorization.

The explicit guidance against meaningless categories like "Other", "Miscellaneous", and "Personal" with clear examples of good vs bad categories demonstrates excellent prompt engineering. This will lead to more actionable email organization suggestions.


429-470: Well-structured label optimization analysis.

The function effectively combines current label data with email content patterns to provide actionable optimization suggestions. The focus on practical time-saving improvements is appropriate.


472-506: Smart dependency on previous analysis results.

The function effectively leverages the user persona analysis to generate targeted recommendations, demonstrating good architectural design where AI functions build upon each other's outputs.


511-542: Solid batching implementation with rate limiting.

The batching logic is well-implemented with appropriate batch sizes, sleep intervals for rate limiting, and clear progress tracking. The early return for empty arrays prevents unnecessary processing.

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

♻️ Duplicate comments (1)
apps/web/utils/actions/report.ts (1)

127-132: Update data sources to reflect actual email fetching

The dataSources object shows 0 for archived and trash, but fetchReceivedEmails actually fetches from these sources. Consider tracking the actual counts from each source for accuracy.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e250fca and 05a8082.

📒 Files selected for processing (8)
  • apps/web/utils/actions/report.ts (1 hunks)
  • apps/web/utils/ai/report/analyze-email-behavior.ts (1 hunks)
  • apps/web/utils/ai/report/analyze-label-optimization.ts (1 hunks)
  • apps/web/utils/ai/report/build-user-persona.ts (1 hunks)
  • apps/web/utils/ai/report/generate-actionable-recommendations.ts (1 hunks)
  • apps/web/utils/ai/report/generate-executive-summary.ts (1 hunks)
  • apps/web/utils/ai/report/response-patterns.ts (1 hunks)
  • apps/web/utils/ai/report/summarize-emails.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (12)
apps/web/**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use @/ for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX

Files:

  • apps/web/utils/ai/report/summarize-emails.ts
  • apps/web/utils/ai/report/analyze-email-behavior.ts
  • apps/web/utils/actions/report.ts
  • apps/web/utils/ai/report/generate-actionable-recommendations.ts
  • apps/web/utils/ai/report/build-user-persona.ts
  • apps/web/utils/ai/report/generate-executive-summary.ts
  • apps/web/utils/ai/report/analyze-label-optimization.ts
  • apps/web/utils/ai/report/response-patterns.ts
!{.cursor/rules/*.mdc}

📄 CodeRabbit Inference Engine (.cursor/rules/cursor-rules.mdc)

Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location

Files:

  • apps/web/utils/ai/report/summarize-emails.ts
  • apps/web/utils/ai/report/analyze-email-behavior.ts
  • apps/web/utils/actions/report.ts
  • apps/web/utils/ai/report/generate-actionable-recommendations.ts
  • apps/web/utils/ai/report/build-user-persona.ts
  • apps/web/utils/ai/report/generate-executive-summary.ts
  • apps/web/utils/ai/report/analyze-label-optimization.ts
  • apps/web/utils/ai/report/response-patterns.ts
**/*.ts

📄 CodeRabbit Inference Engine (.cursor/rules/form-handling.mdc)

**/*.ts: The same validation should be done in the server action too
Define validation schemas using Zod

Files:

  • apps/web/utils/ai/report/summarize-emails.ts
  • apps/web/utils/ai/report/analyze-email-behavior.ts
  • apps/web/utils/actions/report.ts
  • apps/web/utils/ai/report/generate-actionable-recommendations.ts
  • apps/web/utils/ai/report/build-user-persona.ts
  • apps/web/utils/ai/report/generate-executive-summary.ts
  • apps/web/utils/ai/report/analyze-label-optimization.ts
  • apps/web/utils/ai/report/response-patterns.ts
apps/web/utils/{ai,llms}/**/*

📄 CodeRabbit Inference Engine (.cursor/rules/llm.mdc)

apps/web/utils/{ai,llms}/**/*: LLM-related code must be organized in the directories: apps/web/utils/ai/, apps/web/utils/llms/, and apps/web/tests/ for LLM-specific tests.
Keep related AI functions in the same file or directory.

Files:

  • apps/web/utils/ai/report/summarize-emails.ts
  • apps/web/utils/ai/report/analyze-email-behavior.ts
  • apps/web/utils/ai/report/generate-actionable-recommendations.ts
  • apps/web/utils/ai/report/build-user-persona.ts
  • apps/web/utils/ai/report/generate-executive-summary.ts
  • apps/web/utils/ai/report/analyze-label-optimization.ts
  • apps/web/utils/ai/report/response-patterns.ts
apps/web/utils/{ai,llms}/**/*.ts

📄 CodeRabbit Inference Engine (.cursor/rules/llm.mdc)

apps/web/utils/{ai,llms}/**/*.ts: Follow the standard structure for LLM-related functions: use a scoped logger, define a Zod schema for output, validate inputs early, separate system and user prompts, log inputs and outputs, call chatCompletionObject with proper configuration, and return validated results.
Keep system prompts and user prompts separate in LLM-related code.
System prompt should define the LLM's role and task specifications.
User prompt should contain the actual data and context.
Always define a Zod schema for response validation in LLM-related functions.
Make Zod 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 in LLM-related functions.
Include relevant context in log messages for LLM-related code.
Implement early returns for invalid inputs in LLM-related functions.
Use proper error types and logging in LLM-related code.
Implement fallbacks for AI failures in LLM-related functions.
Add retry logic for transient failures using withRetry in LLM-related code.
Use XML-like tags to structure data in LLM prompts.
Remove excessive whitespace and truncate long inputs in LLM prompts.
Format data consistently across similar LLM-related functions.
Use TypeScript types for all parameters and return values in LLM-related code.
Define clear interfaces for complex input/output structures in LLM-related code.
Extract common patterns into utility functions in LLM-related code.
Document complex AI logic with clear comments in LLM-related code.

Files:

  • apps/web/utils/ai/report/summarize-emails.ts
  • apps/web/utils/ai/report/analyze-email-behavior.ts
  • apps/web/utils/ai/report/generate-actionable-recommendations.ts
  • apps/web/utils/ai/report/build-user-persona.ts
  • apps/web/utils/ai/report/generate-executive-summary.ts
  • apps/web/utils/ai/report/analyze-label-optimization.ts
  • apps/web/utils/ai/report/response-patterns.ts
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/logging.mdc)

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

Import Prisma in the project using import prisma from "@/utils/prisma";

**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.

Files:

  • apps/web/utils/ai/report/summarize-emails.ts
  • apps/web/utils/ai/report/analyze-email-behavior.ts
  • apps/web/utils/actions/report.ts
  • apps/web/utils/ai/report/generate-actionable-recommendations.ts
  • apps/web/utils/ai/report/build-user-persona.ts
  • apps/web/utils/ai/report/generate-executive-summary.ts
  • apps/web/utils/ai/report/analyze-label-optimization.ts
  • apps/web/utils/ai/report/response-patterns.ts
apps/web/utils/**

📄 CodeRabbit Inference Engine (.cursor/rules/project-structure.mdc)

Create utility functions in utils/ folder for reusable logic

Files:

  • apps/web/utils/ai/report/summarize-emails.ts
  • apps/web/utils/ai/report/analyze-email-behavior.ts
  • apps/web/utils/actions/report.ts
  • apps/web/utils/ai/report/generate-actionable-recommendations.ts
  • apps/web/utils/ai/report/build-user-persona.ts
  • apps/web/utils/ai/report/generate-executive-summary.ts
  • apps/web/utils/ai/report/analyze-label-optimization.ts
  • apps/web/utils/ai/report/response-patterns.ts
apps/web/utils/**/*.ts

📄 CodeRabbit Inference Engine (.cursor/rules/project-structure.mdc)

apps/web/utils/**/*.ts: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size

Files:

  • apps/web/utils/ai/report/summarize-emails.ts
  • apps/web/utils/ai/report/analyze-email-behavior.ts
  • apps/web/utils/actions/report.ts
  • apps/web/utils/ai/report/generate-actionable-recommendations.ts
  • apps/web/utils/ai/report/build-user-persona.ts
  • apps/web/utils/ai/report/generate-executive-summary.ts
  • apps/web/utils/ai/report/analyze-label-optimization.ts
  • apps/web/utils/ai/report/response-patterns.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)

**/*.{js,jsx,ts,tsx}: Don't use elements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...

Files:

  • apps/web/utils/ai/report/summarize-emails.ts
  • apps/web/utils/ai/report/analyze-email-behavior.ts
  • apps/web/utils/actions/report.ts
  • apps/web/utils/ai/report/generate-actionable-recommendations.ts
  • apps/web/utils/ai/report/build-user-persona.ts
  • apps/web/utils/ai/report/generate-executive-summary.ts
  • apps/web/utils/ai/report/analyze-label-optimization.ts
  • apps/web/utils/ai/report/response-patterns.ts
!pages/_document.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)

!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.

Files:

  • apps/web/utils/ai/report/summarize-emails.ts
  • apps/web/utils/ai/report/analyze-email-behavior.ts
  • apps/web/utils/actions/report.ts
  • apps/web/utils/ai/report/generate-actionable-recommendations.ts
  • apps/web/utils/ai/report/build-user-persona.ts
  • apps/web/utils/ai/report/generate-executive-summary.ts
  • apps/web/utils/ai/report/analyze-label-optimization.ts
  • apps/web/utils/ai/report/response-patterns.ts
apps/web/utils/actions/**/*.ts

📄 CodeRabbit Inference Engine (apps/web/CLAUDE.md)

apps/web/utils/actions/**/*.ts: Use server actions for all mutations (create/update/delete operations)
next-safe-action provides centralized error handling
Use Zod schemas for validation on both client and server
Use revalidatePath in server actions for cache invalidation

apps/web/utils/actions/**/*.ts: Use server actions (with next-safe-action) for all mutations (create/update/delete operations); do NOT use POST API routes for mutations.
Use revalidatePath in server actions to invalidate cache after mutations.

Files:

  • apps/web/utils/actions/report.ts
apps/web/utils/actions/*.ts

📄 CodeRabbit Inference Engine (.cursor/rules/server-actions.mdc)

apps/web/utils/actions/*.ts: Implement all server actions using the next-safe-action library for type safety, input validation, context management, and error handling. Refer to apps/web/utils/actions/safe-action.ts for client definitions (actionClient, actionClientUser, adminActionClient).
Use actionClientUser when only authenticated user context (userId) is needed.
Use actionClient when both authenticated user context and a specific emailAccountId are needed. The emailAccountId must be bound when calling the action from the client.
Use adminActionClient for actions restricted to admin users.
Access necessary context (like userId, emailAccountId, etc.) provided by the safe action client via the ctx object in the .action() handler.
Server Actions are strictly for mutations (operations that change data, e.g., creating, updating, deleting). Do NOT use Server Actions for data fetching (GET operations). For data fetching, use dedicated GET API Routes combined with SWR Hooks.
Use SafeError for expected/handled errors within actions if needed. next-safe-action provides centralized error handling.
Use the .metadata({ name: "actionName" }) method to provide a meaningful name for monitoring. Sentry instrumentation is automatically applied via withServerActionInstrumentation within the safe action clients.
If an action modifies data displayed elsewhere, use revalidatePath or revalidateTag from next/cache within the action handler as needed.

Server action files must start with use server

Files:

  • apps/web/utils/actions/report.ts
🧠 Learnings (8)
apps/web/utils/ai/report/summarize-emails.ts (11)

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Implement fallbacks for AI failures in LLM-related functions.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Document complex AI logic with clear comments in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Extract common patterns into utility functions in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Follow the standard structure for LLM-related functions: use a scoped logger, define a Zod schema for output, validate inputs early, separate system and user prompts, log inputs and outputs, call chatCompletionObject with proper configuration, and return validated results.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Include relevant context in log messages for LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Format data consistently across similar LLM-related functions.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use descriptive scoped loggers for each LLM feature.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Define clear interfaces for complex input/output structures in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Make Zod schemas as specific as possible to guide the LLM output.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use TypeScript types for all parameters and return values in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Always define a Zod schema for response validation in LLM-related functions.

apps/web/utils/ai/report/analyze-email-behavior.ts (12)

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Implement fallbacks for AI failures in LLM-related functions.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Document complex AI logic with clear comments in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Extract common patterns into utility functions in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Follow the standard structure for LLM-related functions: use a scoped logger, define a Zod schema for output, validate inputs early, separate system and user prompts, log inputs and outputs, call chatCompletionObject with proper configuration, and return validated results.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Format data consistently across similar LLM-related functions.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Define clear interfaces for complex input/output structures in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : System prompt should define the LLM's role and task specifications.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use TypeScript types for all parameters and return values in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use XML-like tags to structure data in LLM prompts.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Keep system prompts and user prompts separate in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Always define a Zod schema for response validation in LLM-related functions.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Make Zod schemas as specific as possible to guide the LLM output.

apps/web/utils/actions/report.ts (19)

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-07-18T17:27:58.249Z
Learning: Applies to apps/web/utils/actions/*.ts : Use actionClient when both authenticated user context and a specific emailAccountId are needed. The emailAccountId must be bound when calling the action from the client.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-07-18T17:27:58.249Z
Learning: Applies to apps/web/utils/actions/*.ts : Implement all server actions using the next-safe-action library for type safety, input validation, context management, and error handling. Refer to apps/web/utils/actions/safe-action.ts for client definitions (actionClient, actionClientUser, adminActionClient).

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-07-18T15:05:34.899Z
Learning: Applies to apps/web/utils/gmail/**/*.ts : Keep provider-specific implementation details isolated in the appropriate utils subfolder (e.g., 'apps/web/utils/gmail/')

Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/utils/actions/**/*.ts : Use server actions for all mutations (create/update/delete operations)

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use proper error types and logging in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-07-18T15:04:30.467Z
Learning: Applies to apps/web/app/api/**/route.ts : Use withEmailAccount for email-account-level operations

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-07-19T17:50:28.270Z
Learning: The utils folder also contains core app logic such as Next.js Server Actions and Gmail API requests.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-07-18T17:27:58.249Z
Learning: Applies to apps/web/utils/actions/*.ts : Use the .metadata({ name: "actionName" }) method to provide a meaningful name for monitoring. Sentry instrumentation is automatically applied via withServerActionInstrumentation within the safe action clients.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/project-structure.mdc:0-0
Timestamp: 2025-07-20T09:00:16.505Z
Learning: Applies to apps/web/utils/actions/*.ts : Server action files must start with use server

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-07-18T15:05:16.146Z
Learning: Applies to apps/web/utils/actions/**/*.ts : Use server actions (with next-safe-action) for all mutations (create/update/delete operations); do NOT use POST API routes for mutations.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : User prompt should contain the actual data and context.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Format data consistently across similar LLM-related functions.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Implement fallbacks for AI failures in LLM-related functions.

Learnt from: elie222
PR: #537
File: apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx:30-34
Timestamp: 2025-07-08T13:14:07.449Z
Learning: The clean onboarding page in apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx is intentionally Gmail-specific and should show an error for non-Google email accounts rather than attempting to support multiple providers.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Implement early returns for invalid inputs in LLM-related functions.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Document complex AI logic with clear comments in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/index.mdc:0-0
Timestamp: 2025-07-18T15:05:56.644Z
Learning: Guidelines for working with Gmail API

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-07-18T17:27:58.249Z
Learning: Applies to apps/web/utils/actions/*.ts : Access necessary context (like userId, emailAccountId, etc.) provided by the safe action client via the ctx object in the .action() handler.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Follow the standard structure for LLM-related functions: use a scoped logger, define a Zod schema for output, validate inputs early, separate system and user prompts, log inputs and outputs, call chatCompletionObject with proper configuration, and return validated results.

apps/web/utils/ai/report/generate-actionable-recommendations.ts (10)

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Follow the standard structure for LLM-related functions: use a scoped logger, define a Zod schema for output, validate inputs early, separate system and user prompts, log inputs and outputs, call chatCompletionObject with proper configuration, and return validated results.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Make Zod schemas as specific as possible to guide the LLM output.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Implement fallbacks for AI failures in LLM-related functions.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Document complex AI logic with clear comments in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Always define a Zod schema for response validation in LLM-related functions.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : System prompt should define the LLM's role and task specifications.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Extract common patterns into utility functions in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Define clear interfaces for complex input/output structures in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Format data consistently across similar LLM-related functions.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : User prompt should contain the actual data and context.

apps/web/utils/ai/report/build-user-persona.ts (12)

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : System prompt should define the LLM's role and task specifications.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : User prompt should contain the actual data and context.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Keep system prompts and user prompts separate in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Follow the standard structure for LLM-related functions: use a scoped logger, define a Zod schema for output, validate inputs early, separate system and user prompts, log inputs and outputs, call chatCompletionObject with proper configuration, and return validated results.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Implement fallbacks for AI failures in LLM-related functions.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Define clear interfaces for complex input/output structures in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Extract common patterns into utility functions in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Document complex AI logic with clear comments in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use TypeScript types for all parameters and return values in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Format data consistently across similar LLM-related functions.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Always define a Zod schema for response validation in LLM-related functions.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Make Zod schemas as specific as possible to guide the LLM output.

apps/web/utils/ai/report/generate-executive-summary.ts (10)

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Make Zod schemas as specific as possible to guide the LLM output.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : System prompt should define the LLM's role and task specifications.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Implement fallbacks for AI failures in LLM-related functions.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Follow the standard structure for LLM-related functions: use a scoped logger, define a Zod schema for output, validate inputs early, separate system and user prompts, log inputs and outputs, call chatCompletionObject with proper configuration, and return validated results.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Document complex AI logic with clear comments in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Define clear interfaces for complex input/output structures in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Always define a Zod schema for response validation in LLM-related functions.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use TypeScript types for all parameters and return values in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Format data consistently across similar LLM-related functions.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Extract common patterns into utility functions in LLM-related code.

apps/web/utils/ai/report/analyze-label-optimization.ts (10)

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Follow the standard structure for LLM-related functions: use a scoped logger, define a Zod schema for output, validate inputs early, separate system and user prompts, log inputs and outputs, call chatCompletionObject with proper configuration, and return validated results.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Make Zod schemas as specific as possible to guide the LLM output.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Always define a Zod schema for response validation in LLM-related functions.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Implement fallbacks for AI failures in LLM-related functions.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Document complex AI logic with clear comments in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Define clear interfaces for complex input/output structures in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Format data consistently across similar LLM-related functions.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : System prompt should define the LLM's role and task specifications.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Extract common patterns into utility functions in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use XML-like tags to structure data in LLM prompts.

apps/web/utils/ai/report/response-patterns.ts (10)

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Always define a Zod schema for response validation in LLM-related functions.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Follow the standard structure for LLM-related functions: use a scoped logger, define a Zod schema for output, validate inputs early, separate system and user prompts, log inputs and outputs, call chatCompletionObject with proper configuration, and return validated results.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Make Zod schemas as specific as possible to guide the LLM output.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Implement fallbacks for AI failures in LLM-related functions.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Extract common patterns into utility functions in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Document complex AI logic with clear comments in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use XML-like tags to structure data in LLM prompts.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Define clear interfaces for complex input/output structures in LLM-related code.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : System prompt should define the LLM's role and task specifications.

Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-07-18T15:06:40.272Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Keep system prompts and user prompts separate in LLM-related code.

🧬 Code Graph Analysis (6)
apps/web/utils/ai/report/analyze-email-behavior.ts (3)
apps/web/utils/ai/report/summarize-emails.ts (1)
  • EmailSummary (19-19)
apps/web/utils/llms/types.ts (1)
  • EmailAccountWithAI (10-24)
apps/web/utils/llms/index.ts (1)
  • chatCompletionObject (107-111)
apps/web/utils/ai/report/generate-actionable-recommendations.ts (4)
apps/web/utils/ai/report/summarize-emails.ts (1)
  • EmailSummary (19-19)
apps/web/utils/llms/types.ts (1)
  • EmailAccountWithAI (10-24)
apps/web/utils/ai/report/build-user-persona.ts (1)
  • UserPersona (17-17)
apps/web/utils/llms/index.ts (1)
  • chatCompletionObject (107-111)
apps/web/utils/ai/report/build-user-persona.ts (3)
apps/web/utils/ai/report/summarize-emails.ts (1)
  • EmailSummary (19-19)
apps/web/utils/llms/types.ts (1)
  • EmailAccountWithAI (10-24)
apps/web/utils/llms/index.ts (1)
  • chatCompletionObject (107-111)
apps/web/utils/ai/report/generate-executive-summary.ts (3)
apps/web/utils/ai/report/summarize-emails.ts (1)
  • EmailSummary (19-19)
apps/web/utils/llms/types.ts (1)
  • EmailAccountWithAI (10-24)
apps/web/utils/llms/index.ts (1)
  • chatCompletionObject (107-111)
apps/web/utils/ai/report/analyze-label-optimization.ts (3)
apps/web/utils/ai/report/summarize-emails.ts (1)
  • EmailSummary (19-19)
apps/web/utils/llms/types.ts (1)
  • EmailAccountWithAI (10-24)
apps/web/utils/llms/index.ts (1)
  • chatCompletionObject (107-111)
apps/web/utils/ai/report/response-patterns.ts (3)
apps/web/utils/ai/report/summarize-emails.ts (1)
  • EmailSummary (19-19)
apps/web/utils/llms/types.ts (1)
  • EmailAccountWithAI (10-24)
apps/web/utils/llms/index.ts (1)
  • chatCompletionObject (107-111)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Static Code Analysis Js
  • GitHub Check: Jit Security
🔇 Additional comments (18)
apps/web/utils/ai/report/generate-actionable-recommendations.ts (3)

1-5: LGTM!

The imports follow the established patterns and include all necessary dependencies for the LLM function.


7-32: Well-structured Zod schema for actionable recommendations.

The schema effectively guides the LLM output with specific categories and detailed field descriptions. The enum constraints for difficulty and impact levels provide clear guidance for structured responses.


39-56: Well-structured system and user prompts.

The prompts follow LLM guidelines effectively:

  • System prompt clearly defines the AI's role and task specifications
  • User prompt contains actual data and context
  • Good separation between system and user prompts
  • Specific instructions for categorizing recommendations by timeline
apps/web/utils/ai/report/analyze-email-behavior.ts (3)

1-4: LGTM!

The imports are properly structured and include all necessary dependencies for the LLM function.


6-23: Well-designed schema for email behavior analysis.

The schema effectively structures the expected output with clear categories for timing patterns, content preferences, and engagement triggers. The descriptive field descriptions will guide the LLM to produce structured, useful insights.


30-54: Well-structured prompts for email behavior analysis.

The prompts effectively follow LLM guidelines:

  • System prompt clearly defines the AI's expertise and focus
  • User prompt includes actual email data and context
  • Good handling of optional sent email summaries
  • Clear instructions for pattern identification
apps/web/utils/ai/report/summarize-emails.ts (5)

1-9: LGTM!

Proper implementation of scoped logger and all necessary imports following LLM guidelines.


11-19: Well-defined schema for email summaries.

The schema effectively defines the expected structure with descriptive fields that will guide the LLM to produce consistent, structured summaries.


21-52: Excellent implementation following LLM guidelines.

This function demonstrates proper LLM patterns:

  • Early input validation with logging
  • Batch processing to manage API load and rate limits
  • Proper delay handling between batches
  • Good error handling structure

The batch size of 15 and 1-second delay are reasonable for API rate limiting.


54-96: Exemplary LLM function implementation.

This function demonstrates all required LLM guidelines:

  • Proper input/output logging with trace level
  • Well-separated system and user prompts
  • System prompt defines role and rules clearly
  • User prompt uses structured formatting with XML-like tags
  • Proper schema validation and error handling
  • Consistent data formatting with character limits

This serves as a good reference implementation for other LLM functions.


60-80: Well-crafted prompts with clear guidelines.

The prompts effectively guide the LLM:

  • System prompt provides comprehensive rules including objectivity requirements
  • Good filtering guidelines for relevant vs. promotional content
  • User prompt uses structured XML-like formatting
  • Batch context helps with processing tracking
  • Character limit prevents overly long inputs
apps/web/utils/ai/report/analyze-label-optimization.ts (3)

1-5: LGTM!

The imports properly include all necessary dependencies including Gmail API types for label analysis.


7-18: Well-structured schema for label optimization.

The schema effectively defines optimization suggestions with:

  • Clear enum constraints for optimization types
  • Impact level categorization
  • Descriptive field descriptions that guide LLM output

25-49: Well-structured prompts for label optimization.

The prompts effectively follow LLM guidelines:

  • System prompt clearly defines expertise and focus area
  • User prompt includes actual Gmail label data and email content
  • Clear categorization of optimization types
  • Structured formatting for better LLM comprehension
apps/web/utils/ai/report/build-user-persona.ts (3)

1-4: LGTM!

The imports are properly structured and include all necessary dependencies for the persona building function.


6-17: Well-designed schema for user persona analysis.

The schema effectively captures professional identity with supporting evidence and current priorities. The nested structure and descriptive fields will guide the LLM to produce structured, actionable persona insights.


26-64: Excellent prompt structure with good optional data handling.

The prompts effectively follow LLM guidelines:

  • System prompt clearly defines the AI's analytical role and focus areas
  • User prompt includes structured data sections with good conditional formatting
  • Excellent handling of optional sent emails, signature, and templates
  • Clear instructions for professional identity and priority identification

The conditional prompt construction is particularly well-implemented.

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

26-31: LGTM!

The server action is correctly implemented using actionClient with proper metadata and context access.

Comment on lines +153 to +216
// TODO: should be able to import this functionality from elsewhere
async function fetchGmailLabels(
gmail: gmail_v1.Gmail,
): Promise<gmail_v1.Schema$Label[]> {
try {
const response = await gmail.users.labels.list({ userId: "me" });

const userLabels =
response.data.labels?.filter(
(label: gmail_v1.Schema$Label) =>
label.type === "user" &&
label.name &&
!label.name.startsWith("CATEGORY_") &&
!label.name.startsWith("CHAT"),
) || [];

const labelsWithCounts = await Promise.all(
userLabels
.filter(
(
label,
): label is gmail_v1.Schema$Label & { id: string; name: string } =>
Boolean(label.id && label.name),
)
.map(async (label) => {
try {
const labelDetail = await gmail.users.labels.get({
userId: "me",
id: label.id,
});
return {
...label,
messagesTotal: labelDetail.data.messagesTotal || 0,
messagesUnread: labelDetail.data.messagesUnread || 0,
threadsTotal: labelDetail.data.threadsTotal || 0,
threadsUnread: labelDetail.data.threadsUnread || 0,
};
} catch (error) {
logger.warn(`Failed to get details for label ${label.name}:`, {
error: error instanceof Error ? error.message : String(error),
});
return {
...label,
messagesTotal: 0,
messagesUnread: 0,
threadsTotal: 0,
threadsUnread: 0,
};
}
}),
);

const sortedLabels = labelsWithCounts.sort(
(a, b) => (b.messagesTotal || 0) - (a.messagesTotal || 0),
);

return sortedLabels;
} catch (error) {
logger.warn("Failed to fetch Gmail labels:", {
error: error instanceof Error ? error.message : String(error),
});
return [];
}
}
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

Move Gmail-specific functionality to utils/gmail directory

The TODO comment correctly identifies that this Gmail-specific functionality should be imported from elsewhere. According to the coding guidelines, Gmail-related code should be organized in apps/web/utils/gmail/.

Consider creating a shared module like apps/web/utils/gmail/labels.ts to export this functionality:

// apps/web/utils/gmail/labels.ts
export async function fetchGmailLabels(gmail: gmail_v1.Gmail): Promise<gmail_v1.Schema$Label[]> {
  // Move the implementation here
}

Then import it in this file:

+import { fetchGmailLabels } from "@/utils/gmail/labels";
-// TODO: should be able to import this functionality from elsewhere
-async function fetchGmailLabels(
-  gmail: gmail_v1.Gmail,
-): Promise<gmail_v1.Schema$Label[]> {
-  // ... implementation
-}
🤖 Prompt for AI Agents
In apps/web/utils/actions/report.ts between lines 153 and 216, the
Gmail-specific function fetchGmailLabels should be moved to a dedicated module
under apps/web/utils/gmail/, for example apps/web/utils/gmail/labels.ts. Create
this new file, move the entire fetchGmailLabels function implementation there,
export it, and then import and use it in the original file. This will organize
Gmail-related code according to the project's coding guidelines.

Comment on lines +218 to +254
// TODO: should be able to import this functionality from elsewhere
async function fetchGmailSignature(gmail: gmail_v1.Gmail): Promise<string> {
try {
const sendAsList = await gmail.users.settings.sendAs.list({
userId: "me",
});

if (!sendAsList.data.sendAs || sendAsList.data.sendAs.length === 0) {
logger.warn("No sendAs settings found");
return "";
}

const primarySendAs = sendAsList.data.sendAs[0];
if (!primarySendAs.sendAsEmail) {
logger.warn("No primary sendAs email found");
return "";
}

const signatureResponse = await gmail.users.settings.sendAs.get({
userId: "me",
sendAsEmail: primarySendAs.sendAsEmail,
});

const signature = signatureResponse.data.signature;
logger.info("Gmail signature fetched successfully", {
hasSignature: !!signature,
sendAsEmail: primarySendAs.sendAsEmail,
});

return signature || "";
} catch (error) {
logger.warn("Failed to fetch Gmail signature:", {
error: error instanceof Error ? error.message : String(error),
});
return "";
}
}
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

Move Gmail signature functionality to utils/gmail directory

Similar to fetchGmailLabels, this Gmail-specific functionality should be moved to the appropriate utils subfolder.

Create a module like apps/web/utils/gmail/signature.ts:

// apps/web/utils/gmail/signature.ts
export async function fetchGmailSignature(gmail: gmail_v1.Gmail): Promise<string> {
  // Move the implementation here
}

Then import it:

+import { fetchGmailSignature } from "@/utils/gmail/signature";
-// TODO: should be able to import this functionality from elsewhere
-async function fetchGmailSignature(gmail: gmail_v1.Gmail): Promise<string> {
-  // ... implementation
-}
🤖 Prompt for AI Agents
In apps/web/utils/actions/report.ts from lines 218 to 254, the
fetchGmailSignature function contains Gmail-specific logic that should be moved
to a dedicated module for better organization. Create a new file at
apps/web/utils/gmail/signature.ts and move the entire fetchGmailSignature
function implementation there, exporting it. Then, update the original file to
import fetchGmailSignature from the new location instead of defining it inline.

Comment on lines 25 to 66
export async function aiAnalyzeEmailBehavior(
emailSummaries: EmailSummary[],
emailAccount: EmailAccountWithAI,
sentEmailSummaries?: EmailSummary[],
) {
const system = `You are an expert AI system that analyzes a user's email behavior to infer timing patterns, content preferences, and automation opportunities.

Focus on identifying patterns that can be automated and providing specific, actionable automation rules that would save time and improve email management efficiency.`;

const prompt = `### Email Analysis Data

**Received Emails:**
${emailSummaries.map((email, i) => `${i + 1}. From: ${email.sender} | Subject: ${email.subject} | Category: ${email.category} | Summary: ${email.summary}`).join("\n")}

${
sentEmailSummaries && sentEmailSummaries.length > 0
? `
**Sent Emails:**
${sentEmailSummaries.map((email, i) => `${i + 1}. To: ${email.sender} | Subject: ${email.subject} | Category: ${email.category} | Summary: ${email.summary}`).join("\n")}
`
: ""
}

---

Analyze the email patterns and identify:
1. Timing patterns (when emails are most active, response preferences)
2. Content preferences (what types of emails they engage with vs avoid)
3. Engagement triggers (what prompts them to take action)
4. Specific automation opportunities with estimated time savings`;

const result = await chatCompletionObject({
userAi: emailAccount.user,
system,
prompt,
schema: emailBehaviorSchema,
userEmail: emailAccount.email,
usageLabel: "email-report-email-behavior",
});

return result.object;
}
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

Missing critical LLM function requirements.

The function is missing several elements required by the LLM coding guidelines:

  1. Scoped logger: Should use createScopedLogger for tracing
  2. Input validation: Should validate inputs early
  3. Logging: Should log inputs and outputs
  4. Error handling: Missing fallbacks for AI failures

Add the missing elements:

+import { createScopedLogger } from "@/utils/logger";
+
+const logger = createScopedLogger("email-behavior-analysis");
+
 export async function aiAnalyzeEmailBehavior(
   emailSummaries: EmailSummary[],
   emailAccount: EmailAccountWithAI,
   sentEmailSummaries?: EmailSummary[],
 ) {
+  // Early input validation
+  if (!emailSummaries.length) {
+    logger.warn("No email summaries provided for behavior analysis");
+    return {
+      timingPatterns: { peakHours: [], responsePreference: "", frequency: "" },
+      contentPreferences: { preferred: [], avoided: [] },
+      engagementTriggers: []
+    };
+  }
+
   const system = `You are an expert AI system...`;
   
   const prompt = `### Email Analysis Data...`;
+
+  logger.trace("Input", { 
+    receivedEmailCount: emailSummaries.length,
+    sentEmailCount: sentEmailSummaries?.length || 0,
+    system, 
+    prompt 
+  });

   const result = await chatCompletionObject({
     userAi: emailAccount.user,
     system,
     prompt,
     schema: emailBehaviorSchema,
     userEmail: emailAccount.email,
     usageLabel: "email-report-email-behavior",
   });

+  logger.trace("Output", { result: result.object });
+
   return result.object;
 }
📝 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
export async function aiAnalyzeEmailBehavior(
emailSummaries: EmailSummary[],
emailAccount: EmailAccountWithAI,
sentEmailSummaries?: EmailSummary[],
) {
const system = `You are an expert AI system that analyzes a user's email behavior to infer timing patterns, content preferences, and automation opportunities.
Focus on identifying patterns that can be automated and providing specific, actionable automation rules that would save time and improve email management efficiency.`;
const prompt = `### Email Analysis Data
**Received Emails:**
${emailSummaries.map((email, i) => `${i + 1}. From: ${email.sender} | Subject: ${email.subject} | Category: ${email.category} | Summary: ${email.summary}`).join("\n")}
${
sentEmailSummaries && sentEmailSummaries.length > 0
? `
**Sent Emails:**
${sentEmailSummaries.map((email, i) => `${i + 1}. To: ${email.sender} | Subject: ${email.subject} | Category: ${email.category} | Summary: ${email.summary}`).join("\n")}
`
: ""
}
---
Analyze the email patterns and identify:
1. Timing patterns (when emails are most active, response preferences)
2. Content preferences (what types of emails they engage with vs avoid)
3. Engagement triggers (what prompts them to take action)
4. Specific automation opportunities with estimated time savings`;
const result = await chatCompletionObject({
userAi: emailAccount.user,
system,
prompt,
schema: emailBehaviorSchema,
userEmail: emailAccount.email,
usageLabel: "email-report-email-behavior",
});
return result.object;
}
import { createScopedLogger } from "@/utils/logger";
const logger = createScopedLogger("email-behavior-analysis");
export async function aiAnalyzeEmailBehavior(
emailSummaries: EmailSummary[],
emailAccount: EmailAccountWithAI,
sentEmailSummaries?: EmailSummary[],
) {
// Early input validation
if (!emailSummaries.length) {
logger.warn("No email summaries provided for behavior analysis");
return {
timingPatterns: { peakHours: [], responsePreference: "", frequency: "" },
contentPreferences: { preferred: [], avoided: [] },
engagementTriggers: []
};
}
const system = `You are an expert AI system that analyzes a user's email behavior to infer timing patterns, content preferences, and automation opportunities.
Focus on identifying patterns that can be automated and providing specific, actionable automation rules that would save time and improve email management efficiency.`;
const prompt = `### Email Analysis Data
**Received Emails:**
${emailSummaries
.map(
(email, i) =>
`${i + 1}. From: ${email.sender} | Subject: ${email.subject} | Category: ${email.category} | Summary: ${email.summary}`
)
.join("\n")}
${
sentEmailSummaries && sentEmailSummaries.length > 0
? `
**Sent Emails:**
${sentEmailSummaries
.map(
(email, i) =>
`${i + 1}. To: ${email.sender} | Subject: ${email.subject} | Category: ${email.category} | Summary: ${email.summary}`
)
.join("\n")}
`
: ""
}
---
Analyze the email patterns and identify:
1. Timing patterns (when emails are most active, response preferences)
2. Content preferences (what types of emails they engage with vs avoid)
3. Engagement triggers (what prompts them to take action)
4. Specific automation opportunities with estimated time savings`;
logger.trace("Input", {
receivedEmailCount: emailSummaries.length,
sentEmailCount: sentEmailSummaries?.length || 0,
system,
prompt,
});
const result = await chatCompletionObject({
userAi: emailAccount.user,
system,
prompt,
schema: emailBehaviorSchema,
userEmail: emailAccount.email,
usageLabel: "email-report-email-behavior",
});
logger.trace("Output", { result: result.object });
return result.object;
}
🤖 Prompt for AI Agents
In apps/web/utils/ai/report/analyze-email-behavior.ts around lines 25 to 66, the
function aiAnalyzeEmailBehavior lacks a scoped logger, input validation, logging
of inputs and outputs, and error handling for AI failures. Fix this by importing
and using createScopedLogger to create a scoped logger instance, validate the
inputs at the start of the function to ensure they meet expected types and
conditions, add logging statements to record the received inputs and the AI
response, and wrap the AI call in a try-catch block to handle errors gracefully
with fallback behavior and error logging.

Comment on lines 20 to 61
export async function aiAnalyzeLabelOptimization(
emailSummaries: EmailSummary[],
emailAccount: EmailAccountWithAI,
gmailLabels: gmail_v1.Schema$Label[],
): Promise<z.infer<typeof labelAnalysisSchema>> {
const system = `You are a Gmail organization expert. Analyze the user's current labels and email patterns to suggest specific optimizations that will improve their email organization and workflow efficiency.

Focus on practical suggestions that will reduce email management time and improve organization.`;

const prompt = `### Current Gmail Labels
${gmailLabels.map((label) => `- ${label.name}: ${label.messagesTotal || 0} emails, ${label.messagesUnread || 0} unread`).join("\n")}

### Email Content Analysis
${emailSummaries
.slice(0, 30)
.map(
(email, i) =>
`${i + 1}. From: ${email.sender} | Subject: ${email.subject} | Category: ${email.category} | Summary: ${email.summary}`,
)
.join("\n")}

---

Based on the current labels and email content, suggest specific optimizations:
1. Labels to create based on email patterns
2. Labels to consolidate that have overlapping purposes
3. Labels to rename for better clarity
4. Labels to delete that are unused or redundant

Each suggestion should include the reason and expected impact.`;

const result = await chatCompletionObject({
userAi: emailAccount.user,
system,
prompt,
schema: labelAnalysisSchema,
userEmail: emailAccount.email,
usageLabel: "email-report-label-analysis",
});

return result.object;
}
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

Missing critical LLM function requirements.

The function is missing several elements required by the LLM coding guidelines:

  1. Scoped logger: Should use createScopedLogger for tracing
  2. Input validation: Should validate inputs early
  3. Logging: Should log inputs and outputs
  4. Error handling: Missing fallbacks for AI failures

Add the missing elements:

+import { createScopedLogger } from "@/utils/logger";
+
+const logger = createScopedLogger("label-optimization");
+
 export async function aiAnalyzeLabelOptimization(
   emailSummaries: EmailSummary[],
   emailAccount: EmailAccountWithAI,
   gmailLabels: gmail_v1.Schema$Label[],
 ): Promise<z.infer<typeof labelAnalysisSchema>> {
+  // Early input validation
+  if (!emailSummaries.length && !gmailLabels.length) {
+    logger.warn("No data provided for label optimization analysis");
+    return { optimizationSuggestions: [] };
+  }
+
   const system = `You are a Gmail organization expert...`;
   
   const prompt = `### Current Gmail Labels...`;
+
+  logger.trace("Input", { 
+    emailCount: emailSummaries.length,
+    labelCount: gmailLabels.length,
+    system, 
+    prompt 
+  });

   const result = await chatCompletionObject({
     userAi: emailAccount.user,
     system,
     prompt,
     schema: labelAnalysisSchema,
     userEmail: emailAccount.email,
     usageLabel: "email-report-label-analysis",
   });

+  logger.trace("Output", { result: result.object });
+
   return result.object;
 }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In apps/web/utils/ai/report/analyze-label-optimization.ts between lines 20 and
61, the function lacks a scoped logger, input validation, logging of inputs and
outputs, and error handling for AI failures. Fix this by importing and using
createScopedLogger to create a logger scoped to this function, validate the
inputs early using appropriate schema validation or checks, log the received
inputs before processing and the AI response after receiving it, and wrap the AI
call in a try-catch block to handle errors gracefully with fallback behavior or
error logging.

Comment on lines 19 to 76
export async function aiBuildUserPersona(
emailSummaries: EmailSummary[],
emailAccount: EmailAccountWithAI,
sentEmailSummaries?: EmailSummary[],
gmailSignature?: string,
gmailTemplates?: string[],
): Promise<z.infer<typeof userPersonaSchema>> {
const system = `You are a highly skilled AI analyst tasked with generating a focused professional persona of a user based on their email activity.

Analyze the email summaries, signatures, and templates to identify:
1. Professional identity with supporting evidence
2. Current professional priorities based on email content

Focus on understanding the user's role and what they're currently focused on professionally.`;

const prompt = `### Input Data

**Received Email Summaries:**
${emailSummaries.map((summary, index) => `Email ${index + 1} Summary: ${summary.summary} (Category: ${summary.category})`).join("\n")}

${
sentEmailSummaries && sentEmailSummaries.length > 0
? `
**Sent Email Summaries:**
${sentEmailSummaries.map((summary, index) => `Sent ${index + 1} Summary: ${summary.summary} (Category: ${summary.category})`).join("\n")}
`
: ""
}

**User's Signature:**
${gmailSignature || "[No signature data available – analyze based on email content only]"}

${
gmailTemplates && gmailTemplates.length > 0
? `
**User's Gmail Templates:**
${gmailTemplates.map((template, index) => `Template ${index + 1}: ${template}`).join("\n")}
`
: ""
}

---

Analyze the data and identify:
1. **Professional Identity**: What is their role and what evidence supports this?
2. **Current Priorities**: What are they focused on professionally based on email content?`;

const result = await chatCompletionObject({
userAi: emailAccount.user,
system,
prompt,
schema: userPersonaSchema,
userEmail: emailAccount.email,
usageLabel: "email-report-user-persona",
});

return result.object;
}
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

Missing critical LLM function requirements but good optional parameter handling.

The function is missing several elements required by the LLM coding guidelines:

  1. Scoped logger: Should use createScopedLogger for tracing
  2. Input validation: Should validate inputs early
  3. Logging: Should log inputs and outputs
  4. Error handling: Missing fallbacks for AI failures

However, the optional parameter handling for sent emails, signature, and templates is well-implemented.

Add the missing elements:

+import { createScopedLogger } from "@/utils/logger";
+
+const logger = createScopedLogger("user-persona");
+
 export async function aiBuildUserPersona(
   emailSummaries: EmailSummary[],
   emailAccount: EmailAccountWithAI,
   sentEmailSummaries?: EmailSummary[],
   gmailSignature?: string,
   gmailTemplates?: string[],
 ): Promise<z.infer<typeof userPersonaSchema>> {
+  // Early input validation
+  if (!emailSummaries.length) {
+    logger.warn("No email summaries provided for persona building");
+    return {
+      professionalIdentity: { persona: "", supportingEvidence: [] },
+      currentPriorities: []
+    };
+  }
+
   const system = `You are a highly skilled AI analyst...`;
   
   const prompt = `### Input Data...`;
+
+  logger.trace("Input", { 
+    receivedEmailCount: emailSummaries.length,
+    sentEmailCount: sentEmailSummaries?.length || 0,
+    hasSignature: !!gmailSignature,
+    templateCount: gmailTemplates?.length || 0,
+    system, 
+    prompt 
+  });

   const result = await chatCompletionObject({
     userAi: emailAccount.user,
     system,
     prompt,
     schema: userPersonaSchema,
     userEmail: emailAccount.email,
     usageLabel: "email-report-user-persona",
   });

+  logger.trace("Output", { result: result.object });
+
   return result.object;
 }
📝 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
export async function aiBuildUserPersona(
emailSummaries: EmailSummary[],
emailAccount: EmailAccountWithAI,
sentEmailSummaries?: EmailSummary[],
gmailSignature?: string,
gmailTemplates?: string[],
): Promise<z.infer<typeof userPersonaSchema>> {
const system = `You are a highly skilled AI analyst tasked with generating a focused professional persona of a user based on their email activity.
Analyze the email summaries, signatures, and templates to identify:
1. Professional identity with supporting evidence
2. Current professional priorities based on email content
Focus on understanding the user's role and what they're currently focused on professionally.`;
const prompt = `### Input Data
**Received Email Summaries:**
${emailSummaries.map((summary, index) => `Email ${index + 1} Summary: ${summary.summary} (Category: ${summary.category})`).join("\n")}
${
sentEmailSummaries && sentEmailSummaries.length > 0
? `
**Sent Email Summaries:**
${sentEmailSummaries.map((summary, index) => `Sent ${index + 1} Summary: ${summary.summary} (Category: ${summary.category})`).join("\n")}
`
: ""
}
**User's Signature:**
${gmailSignature || "[No signature data available – analyze based on email content only]"}
${
gmailTemplates && gmailTemplates.length > 0
? `
**User's Gmail Templates:**
${gmailTemplates.map((template, index) => `Template ${index + 1}: ${template}`).join("\n")}
`
: ""
}
---
Analyze the data and identify:
1. **Professional Identity**: What is their role and what evidence supports this?
2. **Current Priorities**: What are they focused on professionally based on email content?`;
const result = await chatCompletionObject({
userAi: emailAccount.user,
system,
prompt,
schema: userPersonaSchema,
userEmail: emailAccount.email,
usageLabel: "email-report-user-persona",
});
return result.object;
}
import { createScopedLogger } from "@/utils/logger";
const logger = createScopedLogger("user-persona");
export async function aiBuildUserPersona(
emailSummaries: EmailSummary[],
emailAccount: EmailAccountWithAI,
sentEmailSummaries?: EmailSummary[],
gmailSignature?: string,
gmailTemplates?: string[],
): Promise<z.infer<typeof userPersonaSchema>> {
// Early input validation
if (!emailSummaries.length) {
logger.warn("No email summaries provided for persona building");
return {
professionalIdentity: { persona: "", supportingEvidence: [] },
currentPriorities: []
};
}
const system = `You are a highly skilled AI analyst tasked with generating a focused professional persona of a user based on their email activity.
Analyze the email summaries, signatures, and templates to identify:
1. Professional identity with supporting evidence
2. Current professional priorities based on email content
Focus on understanding the user's role and what they're currently focused on professionally.`;
const prompt = `### Input Data
**Received Email Summaries:**
${emailSummaries
.map(
(summary, index) =>
`Email ${index + 1} Summary: ${summary.summary} (Category: ${summary.category})`
)
.join("\n")}
${
sentEmailSummaries && sentEmailSummaries.length > 0
? `
**Sent Email Summaries:**
${sentEmailSummaries
.map(
(summary, index) =>
`Sent ${index + 1} Summary: ${summary.summary} (Category: ${summary.category})`
)
.join("\n")}
`
: ""
}
**User's Signature:**
${gmailSignature || "[No signature data available – analyze based on email content only]"}
${
gmailTemplates && gmailTemplates.length > 0
? `
**User's Gmail Templates:**
${gmailTemplates
.map((template, index) => `Template ${index + 1}: ${template}`)
.join("\n")}
`
: ""
}
---
Analyze the data and identify:
1. **Professional Identity**: What is their role and what evidence supports this?
2. **Current Priorities**: What are they focused on professionally based on email content?`;
logger.trace("Input", {
receivedEmailCount: emailSummaries.length,
sentEmailCount: sentEmailSummaries?.length || 0,
hasSignature: !!gmailSignature,
templateCount: gmailTemplates?.length || 0,
system,
prompt,
});
const result = await chatCompletionObject({
userAi: emailAccount.user,
system,
prompt,
schema: userPersonaSchema,
userEmail: emailAccount.email,
usageLabel: "email-report-user-persona",
});
logger.trace("Output", { result: result.object });
return result.object;
}
🤖 Prompt for AI Agents
In apps/web/utils/ai/report/build-user-persona.ts from lines 19 to 76, the
function aiBuildUserPersona lacks critical LLM requirements: add a scoped logger
using createScopedLogger at the start for tracing; validate inputs early to
ensure they meet expected formats or types; add logging statements to record
input parameters and the AI response output; implement error handling with
try-catch around the AI call to provide fallback behavior or error messages if
the AI request fails. Keep the existing optional parameter handling as is.

Comment on lines +48 to +53
export async function aiGenerateExecutiveSummary(
emailSummaries: EmailSummary[],
sentEmailSummaries: EmailSummary[],
gmailLabels: gmail_v1.Schema$Label[],
emailAccount: EmailAccountWithAI,
): Promise<z.infer<typeof executiveSummarySchema>> {
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

Add input validation and early returns

Implement early validation for inputs as required by the coding guidelines.

 export async function aiGenerateExecutiveSummary(
   emailSummaries: EmailSummary[],
   sentEmailSummaries: EmailSummary[],
   gmailLabels: gmail_v1.Schema$Label[],
   emailAccount: EmailAccountWithAI,
 ): Promise<z.infer<typeof executiveSummarySchema>> {
+  // Validate inputs early
+  if (!emailSummaries || emailSummaries.length === 0) {
+    logger.warn("No email summaries provided for executive summary");
+    return {
+      userProfile: {
+        persona: "Unknown",
+        confidence: 0,
+      },
+      topInsights: [],
+      quickActions: [],
+    };
+  }
+
+  logger.info("Starting executive summary generation", {
+    receivedCount: emailSummaries.length,
+    sentCount: sentEmailSummaries.length,
+    labelsCount: gmailLabels.length,
+    emailAccount: emailAccount.email,
+  });
🤖 Prompt for AI Agents
In apps/web/utils/ai/report/generate-executive-summary.ts around lines 48 to 53,
the function aiGenerateExecutiveSummary lacks input validation. Add checks at
the start of the function to validate that emailSummaries, sentEmailSummaries,
gmailLabels, and emailAccount meet expected criteria, and return early or throw
errors if inputs are invalid to prevent further processing with bad data.

Comment on lines 136 to 146
const result = await chatCompletionObject({
userAi: emailAccount.user,
system,
prompt,
schema: executiveSummarySchema,
userEmail: emailAccount.email,
usageLabel: "email-report-executive-summary",
});

return result.object;
}
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

Add error handling, logging, and fallbacks

Implement proper error handling with fallbacks and log the results.

-  const result = await chatCompletionObject({
-    userAi: emailAccount.user,
-    system,
-    prompt,
-    schema: executiveSummarySchema,
-    userEmail: emailAccount.email,
-    usageLabel: "email-report-executive-summary",
-  });
-
-  return result.object;
+  try {
+    const result = await chatCompletionObject({
+      userAi: emailAccount.user,
+      system,
+      prompt,
+      schema: executiveSummarySchema,
+      userEmail: emailAccount.email,
+      usageLabel: "email-report-executive-summary",
+    });
+
+    logger.info("Executive summary generated successfully", {
+      persona: result.object.userProfile.persona,
+      confidence: result.object.userProfile.confidence,
+      insightsCount: result.object.topInsights.length,
+      actionsCount: result.object.quickActions.length,
+    });
+
+    return result.object;
+  } catch (error) {
+    logger.error("Failed to generate executive summary", {
+      error: error instanceof Error ? error.message : String(error),
+    });
+
+    // Return fallback response
+    return {
+      userProfile: {
+        persona: "Professional",
+        confidence: 0,
+      },
+      topInsights: [],
+      quickActions: [],
+    };
+  }
🤖 Prompt for AI Agents
In apps/web/utils/ai/report/generate-executive-summary.ts around lines 136 to
146, the code lacks error handling for the async call to chatCompletionObject.
Wrap the call in a try-catch block, log any errors encountered, and provide a
fallback return value to ensure the function handles failures gracefully without
crashing.

Comment on lines 87 to 97
const result = await chatCompletionObject({
userAi: emailAccount.user,
system,
prompt,
schema: responsePatternsSchema,
userEmail: emailAccount.email,
usageLabel: "email-report-response-patterns",
});

return result.object;
}
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

Add error handling, logging, and fallbacks for AI failures

The function must implement proper error handling with fallbacks and log the results as required by the coding guidelines.

-  const result = await chatCompletionObject({
-    userAi: emailAccount.user,
-    system,
-    prompt,
-    schema: responsePatternsSchema,
-    userEmail: emailAccount.email,
-    usageLabel: "email-report-response-patterns",
-  });
-
-  return result.object;
+  try {
+    const result = await chatCompletionObject({
+      userAi: emailAccount.user,
+      system,
+      prompt,
+      schema: responsePatternsSchema,
+      userEmail: emailAccount.email,
+      usageLabel: "email-report-response-patterns",
+    });
+
+    logger.info("Response pattern analysis completed successfully", {
+      commonResponsesCount: result.object.commonResponses.length,
+      templatesCount: result.object.suggestedTemplates.length,
+      categoriesCount: result.object.categoryOrganization.length,
+    });
+
+    return result.object;
+  } catch (error) {
+    logger.error("Failed to analyze response patterns", {
+      error: error instanceof Error ? error.message : String(error),
+    });
+
+    // Return fallback response
+    return {
+      commonResponses: [],
+      suggestedTemplates: [],
+      categoryOrganization: [],
+    };
+  }
📝 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
const result = await chatCompletionObject({
userAi: emailAccount.user,
system,
prompt,
schema: responsePatternsSchema,
userEmail: emailAccount.email,
usageLabel: "email-report-response-patterns",
});
return result.object;
}
try {
const result = await chatCompletionObject({
userAi: emailAccount.user,
system,
prompt,
schema: responsePatternsSchema,
userEmail: emailAccount.email,
usageLabel: "email-report-response-patterns",
});
logger.info("Response pattern analysis completed successfully", {
commonResponsesCount: result.object.commonResponses.length,
templatesCount: result.object.suggestedTemplates.length,
categoriesCount: result.object.categoryOrganization.length,
});
return result.object;
} catch (error) {
logger.error("Failed to analyze response patterns", {
error: error instanceof Error ? error.message : String(error),
});
// Return fallback response
return {
commonResponses: [],
suggestedTemplates: [],
categoryOrganization: [],
};
}
}
🤖 Prompt for AI Agents
In apps/web/utils/ai/report/response-patterns.ts around lines 87 to 97, the
function calling chatCompletionObject lacks error handling and logging. Wrap the
call in a try-catch block, log any errors encountered, and provide a fallback
return value to ensure the function handles AI failures gracefully according to
coding guidelines.

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

🔭 Outside diff range comments (3)
.cursor/rules/llm.mdc (3)

1-5: Frontmatter: add required globs and make the rule target explicit

Per project rules, frontmatter must include globs that scope where this rule applies. It’s currently empty.

Apply this diff to define explicit targets (aligned with our retrieved learnings and current code organization):

 ---
 description: Guidelines for implementing LLM (Language Model) functionality in the application
-globs: 
+globs:
+  - apps/web/utils/ai/**/*.ts
+  - apps/web/utils/llms/**/*.ts
+  - apps/web/__tests__/**/*.ts
 alwaysApply: false
 ---

Nit: consider renaming this file to a more descriptive kebab-case name, e.g. llm-implementation-guidelines.mdc.


26-31: Fix imports and initialize a scoped logger for the sample code

The example imports UserEmailWithAI (unused) while the function signature uses EmailAccountWithAI. Also, logger is referenced but never defined.

Apply this diff to correct types and define the logger:

 import { z } from "zod";
 import { createScopedLogger } from "@/utils/logger";
 import { chatCompletionObject } from "@/utils/llms";
-import type { UserEmailWithAI } from "@/utils/llms/types";
+import type { EmailAccountWithAI } from "@/utils/llms/types";
+
+const logger = createScopedLogger("featureFunction");

57-71: Inconsistent user reference: userEmail still uses “user” which no longer exists

After switching to emailAccount, userEmail still references user.email. This will not compile in consumers if they follow the snippet literally.

Apply this diff:

   const result = await chatCompletionObject({
-    userAi: emailAccount.user,
+    userAi: emailAccount.user,
     system,
     prompt,
     schema: z.object({
       field1: z.string(),
       field2: z.number(),
       nested: z.object({
         subfield: z.string(),
       }),
       array_field: z.array(z.string()),
     }),
-    userEmail: user.email,
+    userEmail: emailAccount.user.email,
     usageLabel: "Feature Name",
   });

Optional: demonstrate robust error handling/retry as per Best Practices:

// try/catch with retry for transient LLM errors
// const result = await withRetry(() => chatCompletionObject({ ... }), { tries: 3, delayMs: 250 });
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 19e4d87 and 930e874.

📒 Files selected for processing (2)
  • .cursor/rules/llm.mdc (3 hunks)
  • apps/web/utils/ai/knowledge/writing-style.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/utils/ai/knowledge/writing-style.ts
🧰 Additional context used
📓 Path-based instructions (3)
.cursor/rules/*.mdc

📄 CodeRabbit Inference Engine (.cursor/rules/cursor-rules.mdc)

.cursor/rules/*.mdc: Always place rule files in PROJECT_ROOT/.cursor/rules/
Follow the naming convention: use kebab-case for filenames, always use .mdc extension, and make names descriptive of the rule's purpose
Cursor rules must have a frontmatter section with description, globs, and alwaysApply fields, followed by markdown content explaining the rule

Files:

  • .cursor/rules/llm.mdc
!{.cursor/rules/*.mdc}

📄 CodeRabbit Inference Engine (.cursor/rules/cursor-rules.mdc)

Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location

Files:

  • .cursor/rules/llm.mdc
!pages/_document.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/ultracite.mdc)

!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.

Files:

  • .cursor/rules/llm.mdc
🧠 Learnings (6)
📚 Learning: 2025-08-10T22:08:49.243Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-08-10T22:08:49.243Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use TypeScript types for all parameters and return values in LLM-related functions.

Applied to files:

  • .cursor/rules/llm.mdc
📚 Learning: 2025-08-10T22:08:49.243Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-08-10T22:08:49.243Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Use proper error types and logging in LLM-related code.

Applied to files:

  • .cursor/rules/llm.mdc
📚 Learning: 2025-08-10T22:08:49.243Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-08-10T22:08:49.243Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Keep system prompts and user prompts separate in LLM-related functions.

Applied to files:

  • .cursor/rules/llm.mdc
📚 Learning: 2025-08-10T22:08:49.243Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-08-10T22:08:49.243Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Log inputs and outputs with appropriate log levels in LLM-related functions.

Applied to files:

  • .cursor/rules/llm.mdc
📚 Learning: 2025-08-10T22:08:49.243Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-08-10T22:08:49.243Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Implement fallbacks for AI failures in LLM-related functions.

Applied to files:

  • .cursor/rules/llm.mdc
📚 Learning: 2025-08-10T22:08:49.243Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-08-10T22:08:49.243Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Include relevant context in log messages for LLM-related code.

Applied to files:

  • .cursor/rules/llm.mdc
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Static Code Analysis Js
  • GitHub Check: Jit Security

@elie222 elie222 merged commit eed40c3 into elie222:main Aug 14, 2025
7 of 9 checks passed
@edulelis edulelis deleted the persona-report branch August 29, 2025 14:37
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