Skip to content

Follow-up reminders#1234

Closed
jshwrnr wants to merge 13 commits intoelie222:mainfrom
jshwrnr:feat/follow-up-reminders
Closed

Follow-up reminders#1234
jshwrnr wants to merge 13 commits intoelie222:mainfrom
jshwrnr:feat/follow-up-reminders

Conversation

@jshwrnr
Copy link
Collaborator

@jshwrnr jshwrnr commented Jan 8, 2026

Adds automated follow-up reminders for sent emails that haven't received a response.

  • Add follow-up reminder settings UI with enable toggle and days-until-reminder config
  • Create API endpoint to process follow-up reminders and generate draft responses
  • Add Gmail label management for tracking follow-up status (Needs Follow-Up, No Follow-Up Needed)
  • Track outbound emails in reply-tracker for follow-up monitoring
  • Include cleanup utilities for removing outdated follow-up labels
  • Add database migration for followUpDaysReminder setting

Summary by CodeRabbit

  • New Features

    • Settings UI to enable/configure follow-up reminders (toggle, auto-draft, day selectors) with instant UI feedback and toast notifications.
    • Automatic follow-up workflow: threads get a "Follow-up" label when due, optional autogenerated follow-up drafts, and labels cleared when you reply or during cleanup.
  • Chores

    • Server-side processing endpoints, input validation, background processing, and database migrations to persist settings, run reminders, and remove stale drafts.

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

@vercel
Copy link

vercel bot commented Jan 8, 2026

@jshwrnr is attempting to deploy a commit to the Inbox Zero OSS Program Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 8, 2026

📝 Walkthrough

Walkthrough

Adds follow‑up reminders: DB schema + migrations, settings UI and server action, cron API + processor to apply follow‑up labels and optionally generate drafts, label/draft helpers and cleanup utilities, and integrations to remove follow‑up labels during inbound/outbound flows.

Changes

Cohort / File(s) Summary
Settings UI & Integration
apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx, apps/web/app/(app)/[emailAccountId]/assistant/settings/SettingsTab.tsx
New React setting component and dialog; optimistic save via updateFollowUpSettingsAction; component added to SettingsTab.
Cron API & Processor
apps/web/app/api/follow-up-reminders/route.ts, apps/web/app/api/follow-up-reminders/process.ts
New cron-protected GET/POST route and processAllFollowUpReminders(logger) that enumerates eligible accounts, applies follow‑up labels, optionally generates drafts, sets followUpAppliedAt, runs cleanup, and returns a summary.
Server Action & Validation
apps/web/utils/actions/follow-up-reminders.ts, apps/web/utils/actions/follow-up-reminders.validation.ts
New updateFollowUpSettingsAction with Zod validation for follow‑up settings.
Database Schema & Migrations
apps/web/prisma/schema.prisma, apps/web/prisma/migrations/..._add_follow_up_reminders/migration.sql, apps/web/prisma/migrations/..._add_follow_up_auto_draft_setting/migration.sql
Added EmailAccount follow‑up fields and ThreadTracker.followUpAppliedAt; SQL migrations alter tables and add defaults/index.
Follow‑up Label Helpers
apps/web/utils/follow-up/labels.ts, apps/web/utils/label.ts
New FOLLOW_UP_LABEL and helpers: getOrCreateFollowUpLabel, applyFollowUpLabel, removeFollowUpLabel, hasFollowUpLabel; label color mapping updated.
Draft Generation & Cleanup
apps/web/utils/follow-up/generate-draft.ts, apps/web/utils/follow-up/cleanup.ts
generateFollowUpDraft creates AI follow‑up drafts; cleanupStaleDrafts deletes stale follow‑up drafts; clearFollowUpLabel clears labels/timestamps.
Integrations: Inbound/Outbound & Account API
apps/web/utils/reply-tracker/handle-outbound.ts, apps/web/utils/webhook/process-history-item.ts, apps/web/app/api/user/email-account/route.ts
Remove/clear follow‑up label added after inbound/outbound handling; getEmailAccount now returns follow‑up fields.
Misc / Docs
.claude/commands/write-tests.md
Documentation: added "Step 0: Determine Scope" to test-writing workflow guidance.

Sequence Diagram(s)

sequenceDiagram
    participant UI as Settings UI
    participant Action as updateFollowUpSettingsAction
    participant DB as Prisma (EmailAccount)
    participant Cron as Cron/Invoker
    participant API as /api/follow-up-reminders
    participant Processor as processAllFollowUpReminders
    participant Provider as EmailProvider
    participant Draft as Draft Generation
    participant Cleanup as cleanupStaleDrafts

    UI->>Action: save settings
    Action->>DB: update followUp fields
    DB-->>Action: result
    Action-->>UI: success/toast

    Cron->>API: invoke (cron secret)
    API->>Processor: processAllFollowUpReminders(logger)
    Processor->>DB: fetch eligible accounts
    DB-->>Processor: accounts

    loop per account
        Processor->>Provider: init provider
        Processor->>DB: fetch trackers past thresholds (AWAITING / NEEDS_REPLY)
        DB-->>Processor: trackers

        loop AWAITING trackers
            Processor->>Provider: applyFollowUpLabel
            alt auto-draft enabled
                Processor->>Draft: generateFollowUpDraft
            end
            Processor->>DB: set followUpAppliedAt
        end

        loop NEEDS_REPLY trackers
            Processor->>Provider: applyFollowUpLabel
            Processor->>DB: set followUpAppliedAt
        end

        Processor->>Cleanup: cleanupStaleDrafts
    end

    Processor-->>API: summary (total, success, errors)
    API-->>Cron: response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested reviewers

  • baz-reviewer
  • anakarentorosserrano-star

Poem

🐰 I hop through threads to nudge and remind,

I plant little drafts when replies lag behind,
Cron taps my paw and I label with care,
I tidy stale drafts and help threads repair,
A tiny rabbit's hop to keep inboxes fair.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 46.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Follow-up reminders' directly matches the PR's main objective: adding automated follow-up reminders for sent emails without responses, as confirmed by the changeset across UI, API, database, and utility files.

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

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

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

🤖 Fix all issues with AI agents
In
@apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx:
- Around line 40-56: The useAction call binds updateFollowUpSettingsAction to
data?.id ?? "" too early so it captures an empty emailAccountId; fix by not
initializing the component until data is loaded—add a guard like if (!data)
return null or a loader and only then call
useAction(updateFollowUpSettingsAction.bind(null, data.id), {...}); after this
change remove the subsequent defensive if (!data) return; checks around the
execute/isExecuting handlers and keep mutate/toast logic in the
onSuccess/onError callbacks referencing data now that it is guaranteed to exist.

In @apps/web/app/api/follow-up-reminders/process.ts:
- Around line 39-43: Don't log full email addresses for privacy: in the loop
over emailAccounts where you create accountLogger via logger.with (currently
including emailAccount.email and emailAccount.id), remove emailAccount.email and
only include emailAccount.id, or replace the email field with a redacted value
(e.g., deterministic hash or masked/truncated string) produced by a helper
(e.g., hashEmail or maskEmail) before calling logger.with; update any downstream
usage that expects a plain email in accountLogger metadata accordingly.
- Around line 94-98: The code assumes emailAccount.account is always present
before calling createEmailProvider; add a null check for emailAccount.account
(the OAuth account relation) before accessing account.provider and creating the
provider. If emailAccount.account is null, log a clear warning/error via logger
(including emailAccount.id or emailAccount.email), skip or return early from the
process, and avoid calling createEmailProvider; ensure references to
createEmailProvider, emailAccount, and the local provider variable are updated
accordingly to handle the early-exit path.

In @apps/web/utils/follow-up/cleanup.ts:
- Around line 63-67: The code repeatedly calls provider.getDrafts(...) per stale
tracker and then filters into threadDrafts, causing redundant fetches; change
the logic to fetch drafts once per account (call provider.getDrafts({
maxResults: 100 }) a single time outside the tracker loop and reuse the drafts
array) or, if the provider supports server-side filtering, call
provider.getDrafts({ threadId: tracker.threadId, maxResults: 100 }) instead of
fetching all and filtering; update references to drafts and threadDrafts
accordingly and remove per-tracker provider.getDrafts calls so deletion uses the
cached or filtered results.
🧹 Nitpick comments (5)
apps/web/utils/follow-up/generate-draft.ts (1)

58-91: Consider idempotency safeguards.

The implementation is well-structured with proper error handling and logging. However, there's no check to prevent creating duplicate drafts if this function is called multiple times for the same thread.

Given that the schema includes ThreadTracker.followUpAppliedAt for tracking, consider either:

  1. Checking for existing drafts before creating a new one
  2. Documenting that the caller is responsible for idempotency
  3. Updating followUpAppliedAt after draft creation to prevent re-processing
💡 Example: Add idempotency check
  try {
+   // Check if we've already processed this thread
+   const tracker = await prisma.threadTracker.findFirst({
+     where: {
+       emailAccountId,
+       threadId,
+       followUpAppliedAt: { not: null },
+     },
+   });
+   
+   if (tracker) {
+     logger.info("Follow-up draft already exists", { threadId });
+     return;
+   }
+
    // Get the thread to find the last message
    const thread = await provider.getThread(threadId);
apps/web/utils/reply-tracker/handle-outbound.ts (1)

62-73: Consider parallelizing with existing operations.

The follow-up label removal could be included in the Promise.allSettled block at lines 29-48 to execute in parallel with other outbound processing tasks, potentially improving performance. This would maintain the same error isolation while reducing total execution time.

♻️ Proposed refactor
  await Promise.allSettled([
    trackSentDraftStatus({
      emailAccountId: emailAccount.id,
      message,
      provider,
      logger,
    }).catch((error) => {
      logger.error("Error tracking sent draft status", { error });
      captureException(error, { emailAccountId: emailAccount.id });
    }),
    handleOutboundReply({
      emailAccount,
      message,
      provider,
      logger,
    }).catch((error) => {
      logger.error("Error handling outbound reply", { error });
      captureException(error, { emailAccountId: emailAccount.id });
    }),
+    removeFollowUpLabelIfPresent({
+      emailAccountId: emailAccount.id,
+      threadId: message.threadId,
+      provider,
+      logger,
+    }).catch((error) => {
+      logger.error("Error removing follow-up label", { error });
+      captureException(error, { emailAccountId: emailAccount.id });
+    }),
  ]);

  try {
    await cleanupThreadAIDrafts({
      threadId: message.threadId,
      emailAccountId: emailAccount.id,
      provider,
      logger,
    });
  } catch (error) {
    logger.error("Error during thread draft cleanup", { error });
    captureException(error, { emailAccountId: emailAccount.id });
  }
-
-  // Remove follow-up label if present (user replied, so follow-up no longer needed)
-  try {
-    await removeFollowUpLabelIfPresent({
-      emailAccountId: emailAccount.id,
-      threadId: message.threadId,
-      provider,
-      logger,
-    });
-  } catch (error) {
-    logger.error("Error removing follow-up label", { error });
-    captureException(error, { emailAccountId: emailAccount.id });
-  }
apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx (1)

164-181: Consider using SelectValue for better accessibility.

The Select component manually renders the selected label in the SelectTrigger instead of using the built-in SelectValue component. This might cause accessibility issues as screen readers may not properly announce the selected value.

♻️ Proposed refactor
  <Select
    value={awaitingDays.toString()}
    onValueChange={handleAwaitingDaysChange}
    disabled={!enabled || isExecuting}
  >
-    <SelectTrigger id="awaiting-days">
-      {dayOptions.find(
-        (d) => d.value === awaitingDays.toString(),
-      )?.label ?? "Select..."}
-    </SelectTrigger>
+    <SelectTrigger id="awaiting-days" />
    <SelectContent>
      {dayOptions.map((option) => (
        <SelectItem key={option.value} value={option.value}>
          {option.label}
        </SelectItem>
      ))}
    </SelectContent>
  </Select>

Apply the same pattern to the other Select component at lines 192-209.

Note: This assumes the shadcn/ui Select component automatically renders SelectValue when no children are provided in SelectTrigger. If not, add <SelectValue /> explicitly.

apps/web/app/api/follow-up-reminders/process.ts (1)

210-213: Logged counts may be inaccurate.

The logged awaitingProcessed and needsReplyProcessed values reflect the number of trackers found, not the number successfully processed. Consider tracking actual success counts for accurate observability.

apps/web/utils/follow-up/labels.ts (1)

3-5: Consider accepting logger as a parameter per coding guidelines.

Per the coding guidelines for utility functions: "Logger should be passed as a parameter to helper functions instead of creating their own logger instances." This enables better context enrichment and log correlation in the calling code.

Example for applyFollowUpLabel
 export async function applyFollowUpLabel({
   provider,
   threadId,
   messageId,
+  logger,
 }: {
   provider: EmailProvider;
   threadId: string;
   messageId: string;
+  logger: Logger;
 }): Promise<void> {

Copy link
Contributor

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

Choose a reason for hiding this comment

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

3 issues found across 15 files

Prompt for AI agents (all issues)

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


<file name="apps/web/utils/follow-up/cleanup.ts">

<violation number="1" location="apps/web/utils/follow-up/cleanup.ts:64">
P1: N+1 API call issue: `provider.getDrafts()` is called inside the loop for each stale tracker. If there are 10 trackers, this makes 10 identical API calls to fetch the same drafts list. Move the `getDrafts` call outside the loop to fetch once and reuse.</violation>
</file>

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

<violation number="1" location="apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx:52">
P2: Potential runtime error: `error.error.serverError` could throw if `error.error` is undefined. Use optional chaining to safely access nested properties.</violation>
</file>

<file name="apps/web/app/api/follow-up-reminders/process.ts">

<violation number="1" location="apps/web/app/api/follow-up-reminders/process.ts:204">
P2: The `cleanupStaleDrafts` call is not wrapped in try/catch. If cleanup fails, the error propagates up and the account is counted as failed, even though the actual follow-up processing (labels + drafts) succeeded. Consider wrapping this in try/catch to prevent a non-critical cleanup failure from marking the entire account processing as failed.

(Based on your team's feedback about non-critical helper functions having internal error handling so failures don't impact the main flow.) [FEEDBACK_USED]</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@jshwrnr jshwrnr changed the title feat: Follow-up reminders for sent emails Follow-up reminders Jan 8, 2026
@vercel
Copy link

vercel bot commented Jan 8, 2026

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

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

Copy link
Contributor

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

Choose a reason for hiding this comment

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

1 issue found across 3 files (changes from recent commits).

Prompt for AI agents (all issues)

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


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

<violation number="1" location="apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx:57">
P2: Consider removing the `mutate()` call in `onError` to avoid unnecessary refetching on failure. The optimistic update will remain stale, but SWR will eventually revalidate. Alternatively, store the previous state before optimistic updates and restore it on error.

(Based on your team's feedback about avoiding refetching on useAction failure.) [FEEDBACK_USED]</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @apps/web/app/api/follow-up-reminders/process.ts:
- Around line 149-176: The tracker processing block can partially succeed
(labels/draft created) but fail to mark the tracker
(prisma.threadTracker.update), causing duplicate reprocessing; wrap the
per-tracker work in a Prisma transaction so the label/draft creation and the
update are atomic: call prisma.$transaction and run applyFollowUpLabel and
generateFollowUpDraft inside it (or adapt those functions to accept a
transaction client) and then perform tx.threadTracker.update({ where: { id:
tracker.id }, data: { followUpAppliedAt: now } }) within the same transaction,
logging with trackerLogger and capturing exceptions as before.
🧹 Nitpick comments (1)
apps/web/app/api/follow-up-reminders/process.ts (1)

39-57: Consider parallel processing limits for scalability.

The current implementation processes accounts sequentially, which is safe and prevents overwhelming external services. However, for a large number of accounts, consider implementing controlled parallel processing with a concurrency limit (e.g., using Promise.all with batches or a library like p-limit).

💡 Optional enhancement
import pLimit from 'p-limit';

// Process with concurrency limit of 5
const limit = pLimit(5);
const promises = emailAccounts.map(emailAccount =>
  limit(() => processAccountFollowUps({ emailAccount, logger: accountLogger }))
);

const results = await Promise.allSettled(promises);
// Handle results...

This would maintain API rate limit safety while improving throughput for many accounts.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 58a762e and 7a27102.

📒 Files selected for processing (3)
  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/utils/follow-up/cleanup.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/utils/follow-up/cleanup.ts
🧰 Additional context used
📓 Path-based instructions (20)
**/*.{ts,tsx}

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

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

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

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

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

Files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
**/*.{ts,tsx,js,jsx}

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

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

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

Files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
apps/web/**/*.{ts,tsx}

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

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

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

Files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
apps/web/app/api/**/*.{ts,tsx}

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

apps/web/app/api/**/*.{ts,tsx}: API routes must use withAuth, withEmailAccount, or withError middleware for authentication
All database queries must include user scoping with emailAccountId or userId filtering in WHERE clauses
Request parameters must be validated before use; avoid direct parameter usage without type checking
Use generic error messages instead of revealing internal details; throw SafeError instead of exposing user IDs, resource IDs, or system information
API routes should only return necessary fields using select in database queries to prevent unintended information disclosure
Cron endpoints must use hasCronSecret or hasPostCronSecret to validate cron requests and prevent unauthorized access
Request bodies should use Zod schemas for validation to ensure type safety and prevent injection attacks

Files:

  • apps/web/app/api/follow-up-reminders/process.ts
**/app/api/**/*.ts

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

**/app/api/**/*.ts: ALL API routes that handle user data MUST use appropriate middleware: use withEmailAccount for email-scoped operations, use withAuth for user-scoped operations, or use withError with proper validation for public/custom auth endpoints
Use withEmailAccount middleware for operations scoped to a specific email account, including reading/writing emails, rules, schedules, or any operation using emailAccountId
Use withAuth middleware for user-level operations such as user settings, API keys, and referrals that use only userId
Use withError middleware only for public endpoints, custom authentication logic, or cron endpoints. For cron endpoints, MUST use hasCronSecret() or hasPostCronSecret() validation
Cron endpoints without proper authentication can be triggered by anyone. CRITICAL: All cron endpoints MUST validate cron secret using hasCronSecret(request) or hasPostCronSecret(request) and capture unauthorized attempts with captureException()
Always validate request bodies using Zod schemas to ensure type safety and prevent invalid data from reaching database operations
Maintain consistent error response format across all API routes to avoid information disclosure while providing meaningful error feedback

Files:

  • apps/web/app/api/follow-up-reminders/process.ts
**/*.ts

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

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

Files:

  • apps/web/app/api/follow-up-reminders/process.ts
**/*.{tsx,ts}

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

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

Files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
**/*.{tsx,ts,css}

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

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

Files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
**/*.{js,jsx,ts,tsx}

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

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

Files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
!(pages/_document).{jsx,tsx}

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

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

Files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
**/*.{js,ts,jsx,tsx}

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

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

Files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
apps/web/**/*.{ts,tsx,js,jsx}

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

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

Files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
apps/web/app/**/*.{ts,tsx}

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

Follow NextJS app router structure with (app) directory

Files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
apps/web/**/*.{ts,tsx,js,jsx,json,css}

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

Format code with Prettier

Files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
apps/web/**/*.{example,ts,json}

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

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

Files:

  • apps/web/app/api/follow-up-reminders/process.ts
apps/web/app/api/**/*.ts

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

apps/web/app/api/**/*.ts: Create GET API routes wrapped with withAuth or withEmailAccount middleware for fetching data
Export response types from GET API routes using export type GetXResponse = Awaited<ReturnType<typeof getData>>

Files:

  • apps/web/app/api/follow-up-reminders/process.ts
apps/web/app/(app)/**/*.{ts,tsx}

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

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

Files:

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

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

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
**/*.{jsx,tsx}

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

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
apps/web/**/*.{tsx,jsx}

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

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
🧠 Learnings (30)
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:39:04.892Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-11-25T14:39:04.892Z
Learning: Applies to apps/web/app/api/**/route.ts : All database queries must include user/account filtering with `emailAccountId` or `userId` in WHERE clauses to prevent IDOR vulnerabilities

Applied to files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-07-08T13:14:07.449Z
Learnt from: elie222
Repo: elie222/inbox-zero 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.

Applied to files:

  • apps/web/app/api/follow-up-reminders/process.ts
📚 Learning: 2025-11-25T14:39:23.326Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:23.326Z
Learning: Applies to app/api/**/*.ts : ALL API routes that handle user data MUST use appropriate middleware: `withEmailAccount` for email-scoped operations, `withAuth` for user-scoped operations, or `withError` with proper validation for public/cron endpoints

Applied to files:

  • apps/web/app/api/follow-up-reminders/process.ts
📚 Learning: 2025-11-25T14:39:49.448Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-11-25T14:39:49.448Z
Learning: Applies to apps/web/utils/actions/*.ts : Use `actionClient` when both authenticated user context and a specific emailAccountId are needed, with emailAccountId bound when calling from the client

Applied to files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:39:23.326Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:23.326Z
Learning: Applies to **/*.ts : ALL database queries MUST be scoped to the authenticated user/account - include user/account filtering in `where` clauses (e.g., `emailAccountId`, `userId`) to ensure users only access their own resources

Applied to files:

  • apps/web/app/api/follow-up-reminders/process.ts
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : Implement early returns for invalid LLM inputs, use proper error types and logging, implement fallbacks for AI failures, and add retry logic for transient failures using `withRetry`

Applied to files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/utils/actions/**/*.ts : Use proper error handling with try/catch blocks

Applied to files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-12-31T23:49:09.597Z
Learnt from: rsnodgrass
Repo: elie222/inbox-zero PR: 1154
File: apps/web/app/api/user/setup-progress/route.ts:0-0
Timestamp: 2025-12-31T23:49:09.597Z
Learning: In apps/web/app/api/user/setup-progress/route.ts, Reply Zero enabled status should be determined solely by checking if the TO_REPLY rule is enabled, as it is the critical/canonical rule that Reply Zero is based on. The other conversation status types (FYI, AWAITING_REPLY, ACTIONED) should not be checked for determining Reply Zero setup progress.
<!-- </add_learning>

Applied to files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:37:09.306Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-11-25T14:37:09.306Z
Learning: Applies to apps/web/utils/actions/*.ts : Server actions should use 'use server' directive and automatically receive authentication context (`emailAccountId`) from the `actionClient`

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:36:36.276Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-11-25T14:36:36.276Z
Learning: Applies to **/*.{ts,tsx} : Use `result?.serverError` with `toastError` and `toastSuccess` for error handling in server actions

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't use optional chaining where undefined values aren't allowed

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:36:40.146Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-11-25T14:36:40.146Z
Learning: Applies to **/*.{ts,tsx} : Use `result?.serverError` with `toastError` from `@/components/Toast` for error handling in async operations

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/**/*.{tsx,jsx} : Use `result?.serverError` with `toastError` and `toastSuccess` for error handling in forms

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2026-01-07T21:07:06.691Z
Learnt from: elie222
Repo: elie222/inbox-zero PR: 1230
File: apps/web/app/(app)/[emailAccountId]/drive/page.tsx:47-70
Timestamp: 2026-01-07T21:07:06.691Z
Learning: In TSX files across the codebase, prefer direct server action calls with manual error handling (e.g., check result?.serverError and handle errors explicitly). The useAction hook pattern is not required; you can call server actions directly and use try/finally to ensure cleanup (such as resetting loading states) regardless of the hook. Apply consistently for components that perform server interactions.

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:39:23.326Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:23.326Z
Learning: Applies to app/api/**/*.ts : Use `SafeError` for error responses to prevent information disclosure - provide generic messages (e.g., 'Rule not found' not 'Rule {id} does not exist for user {userId}') without revealing internal IDs or ownership details

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:39:27.909Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:27.909Z
Learning: Applies to **/*.ts : Use SafeError for error responses to prevent information disclosure. Generic error messages should not reveal internal IDs, logic, or resource ownership details

Applied to files:

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

Applied to files:

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

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:37:30.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/hooks.mdc:0-0
Timestamp: 2025-11-25T14:37:30.660Z
Learning: Custom hooks should encapsulate reusable stateful logic, especially for data fetching or complex UI interactions

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
🧬 Code graph analysis (1)
apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx (6)
apps/web/utils/actions/follow-up-reminders.ts (1)
  • updateFollowUpSettingsAction (7-30)
apps/web/components/Toast.tsx (2)
  • toastSuccess (3-12)
  • toastError (14-19)
apps/web/components/SettingCard.tsx (1)
  • SettingCard (3-34)
apps/web/components/LoadingContent.tsx (1)
  • LoadingContent (13-31)
apps/web/components/Input.tsx (1)
  • Label (116-132)
apps/web/components/Toggle.tsx (1)
  • Toggle (18-50)
⏰ 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: cubic · AI code reviewer
  • GitHub Check: test
🔇 Additional comments (6)
apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx (3)

63-120: LGTM - Well-implemented handlers with optimistic updates.

The callback handlers follow best practices:

  • Proper guard clauses prevent execution when data is unavailable
  • Optimistic updates use the correct pattern with mutate(optimisticData, false)
  • Number.parseInt includes the radix parameter as required
  • Dependency arrays are complete and accurate

157-161: LGTM - Correct use of LoadingContent.

The component properly uses LoadingContent to handle loading and error states with an appropriate skeleton loader.


45-61: Remove .bind() - actionClient provides emailAccountId from context.

The action uses actionClient which automatically provides emailAccountId from the authenticated context (ctx). Binding it as a parameter is incorrect and won't work as intended. The actionClient pattern doesn't accept emailAccountId as a bound parameter.

🔧 Proposed fix
-  const { execute, isExecuting } = useAction(
-    updateFollowUpSettingsAction.bind(null, data?.id ?? ""),
-    {
+  const { execute, isExecuting } = useAction(updateFollowUpSettingsAction, {
       onSuccess: () => {
         toastSuccess({
           description: "Follow-up reminder settings updated!",
         });
         mutate();
       },
       onError: (error) => {
         mutate();
         toastError({
           description: error.error?.serverError ?? "Failed to update settings",
         });
       },
-    },
-  );
+  });

Based on learnings: actionClient actions receive emailAccountId from the authenticated context automatically, not as bound parameters.

⛔ Skipped due to learnings
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-11-25T14:39:49.448Z
Learning: Applies to apps/web/utils/actions/*.ts : Use `actionClient` when both authenticated user context and a specific emailAccountId are needed, with emailAccountId bound when calling from the client
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-11-25T14:37:09.306Z
Learning: Applies to apps/web/utils/actions/*.ts : Server actions should use 'use server' directive and automatically receive authentication context (`emailAccountId`) from the `actionClient`
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-11-25T14:39:49.448Z
Learning: Applies to apps/web/utils/actions/*.ts : Access context (userId, emailAccountId, etc.) via the `ctx` object parameter in the `.action()` handler
Learnt from: elie222
Repo: elie222/inbox-zero PR: 1230
File: apps/web/app/(app)/[emailAccountId]/drive/page.tsx:47-70
Timestamp: 2026-01-07T21:07:06.691Z
Learning: Direct server action calls with manual error handling (checking result?.serverError) are a valid pattern in this codebase. The useAction hook pattern is not required; developers can call server actions directly and handle errors manually, especially when using try/finally for cleanup operations like resetting loading states.
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-11-25T14:39:49.448Z
Learning: Applies to apps/web/utils/actions/*.ts : Use `actionClientUser` when only authenticated user context (userId) is needed
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:23.326Z
Learning: Applies to app/api/**/*.ts : Use `withEmailAccount` middleware for operations scoped to a specific email account (reading/writing emails, rules, schedules, etc.) - provides `emailAccountId`, `userId`, and `email` in `request.auth`
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-11-25T14:36:40.146Z
Learning: Applies to **/*{.action,.server}.{ts,tsx} : For mutating data, use Next.js server actions
apps/web/app/api/follow-up-reminders/process.ts (3)

16-32: LGTM - Database queries properly scoped.

All database queries correctly include user scoping with emailAccountId filtering in WHERE clauses, preventing unauthorized data access. The premium filter is also properly applied.

Based on learnings: All database queries must include user scoping with emailAccountId or userId filtering in WHERE clauses.

Also applies to: 109-117, 126-134


44-56: LGTM - Comprehensive error handling.

The error handling strategy is well-implemented:

  • Individual failures don't stop batch processing
  • All errors are captured via captureException
  • Cleanup is non-blocking and wrapped in try-catch
  • Logging provides appropriate context at each level

Also applies to: 149-175, 186-204, 208-217


1-1: No changes needed. The import { subDays } from "date-fns/subDays" syntax is correct for date-fns v3+, which uses named exports from submodule files. This import pattern is consistently used throughout the codebase in multiple files.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In
@apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx:
- Around line 64-81: handleToggle can start overlapping requests because it
calls execute() without checking isExecuting; add an early return if isExecuting
is true to prevent concurrent calls (i.e., at the top of handleToggle check if
isExecuting then return), and apply the same guard to handleAwaitingDaysChange
and handleNeedsReplyDaysChange so each handler skips calling execute() while a
request is in flight; keep the optimistic mutate behavior but only call execute
when not isExecuting.
🧹 Nitpick comments (1)
apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx (1)

124-136: Consider showing loading state in the disabled button.

When data is loading, the button shows "Configure" in a disabled state without any loading indicator. Users might not realize the component is still loading data.

♻️ Optional: Add loading indicator
         right={
-          <Button variant="outline" size="sm" disabled>
-            Configure
+          <Button variant="outline" size="sm" disabled>
+            {isLoading ? "Loading..." : "Configure"}
           </Button>
         }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7a27102 and 446f9f1.

📒 Files selected for processing (1)
  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
🧰 Additional context used
📓 Path-based instructions (15)
**/*.{ts,tsx}

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

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

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

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

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
apps/web/app/(app)/**/*.{ts,tsx}

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

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
**/*.{ts,tsx,js,jsx}

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

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

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
apps/web/**/*.{ts,tsx}

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

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

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
**/*.{tsx,ts}

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

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
**/*.{tsx,ts,css}

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

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

Files:

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

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

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
**/*.{js,jsx,ts,tsx}

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

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
**/*.{jsx,tsx}

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

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
!(pages/_document).{jsx,tsx}

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

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
**/*.{js,ts,jsx,tsx}

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

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
apps/web/**/*.{ts,tsx,js,jsx}

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

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
apps/web/app/**/*.{ts,tsx}

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

Follow NextJS app router structure with (app) directory

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
apps/web/**/*.{tsx,jsx}

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

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
apps/web/**/*.{ts,tsx,js,jsx,json,css}

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

Format code with Prettier

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
🧠 Learnings (22)
📚 Learning: 2025-11-25T14:39:49.448Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-11-25T14:39:49.448Z
Learning: Applies to apps/web/utils/actions/*.ts : Use `actionClient` when both authenticated user context and a specific emailAccountId are needed, with emailAccountId bound when calling from the client

Applied to files:

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

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:37:09.306Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-11-25T14:37:09.306Z
Learning: Applies to apps/web/utils/actions/*.ts : Server actions should use 'use server' directive and automatically receive authentication context (`emailAccountId`) from the `actionClient`

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:39:04.892Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-11-25T14:39:04.892Z
Learning: Applies to apps/web/app/api/**/route.ts : All database queries must include user/account filtering with `emailAccountId` or `userId` in WHERE clauses to prevent IDOR vulnerabilities

Applied to files:

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

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:36:36.276Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-11-25T14:36:36.276Z
Learning: Applies to **/*.{ts,tsx} : Use `result?.serverError` with `toastError` and `toastSuccess` for error handling in server actions

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/**/*.{tsx,jsx} : Use `result?.serverError` with `toastError` and `toastSuccess` for error handling in forms

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:36:40.146Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-11-25T14:36:40.146Z
Learning: Applies to **/*.{ts,tsx} : Use `result?.serverError` with `toastError` from `@/components/Toast` for error handling in async operations

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't use optional chaining where undefined values aren't allowed

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:39:23.326Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:23.326Z
Learning: Applies to app/api/**/*.ts : Use `SafeError` for error responses to prevent information disclosure - provide generic messages (e.g., 'Rule not found' not 'Rule {id} does not exist for user {userId}') without revealing internal IDs or ownership details

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:39:27.909Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:27.909Z
Learning: Applies to **/*.ts : Use SafeError for error responses to prevent information disclosure. Generic error messages should not reveal internal IDs, logic, or resource ownership details

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/utils/actions/**/*.ts : Use proper error handling with try/catch blocks

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : Implement early returns for invalid LLM inputs, use proper error types and logging, implement fallbacks for AI failures, and add retry logic for transient failures using `withRetry`

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2026-01-07T21:07:06.691Z
Learnt from: elie222
Repo: elie222/inbox-zero PR: 1230
File: apps/web/app/(app)/[emailAccountId]/drive/page.tsx:47-70
Timestamp: 2026-01-07T21:07:06.691Z
Learning: In TSX files across the codebase, prefer direct server action calls with manual error handling (e.g., check result?.serverError and handle errors explicitly). The useAction hook pattern is not required; you can call server actions directly and use try/finally to ensure cleanup (such as resetting loading states) regardless of the hook. Apply consistently for components that perform server interactions.

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Make sure to pass a message value when creating a built-in error

Applied to files:

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

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:37:30.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/hooks.mdc:0-0
Timestamp: 2025-11-25T14:37:30.660Z
Learning: Custom hooks should encapsulate reusable stateful logic, especially for data fetching or complex UI interactions

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-12-31T23:49:09.597Z
Learnt from: rsnodgrass
Repo: elie222/inbox-zero PR: 1154
File: apps/web/app/api/user/setup-progress/route.ts:0-0
Timestamp: 2025-12-31T23:49:09.597Z
Learning: In apps/web/app/api/user/setup-progress/route.ts, Reply Zero enabled status should be determined solely by checking if the TO_REPLY rule is enabled, as it is the critical/canonical rule that Reply Zero is based on. The other conversation status types (FYI, AWAITING_REPLY, ACTIONED) should not be checked for determining Reply Zero setup progress.
<!-- </add_learning>

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:36:36.276Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-11-25T14:36:36.276Z
Learning: For mutating data, use Next.js server actions instead of SWR

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/hooks/**/*.{ts,tsx} : Call `mutate()` after successful mutations in SWR hooks to refresh data

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:37:09.306Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-11-25T14:37:09.306Z
Learning: Applies to apps/web/hooks/use*.ts : Call `mutate()` on SWR hooks after successful mutations to refresh cached data

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:36:40.146Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-11-25T14:36:40.146Z
Learning: Applies to **/*{.action,.server}.{ts,tsx} : For mutating data, use Next.js server actions

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test
🔇 Additional comments (3)
apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx (3)

1-35: LGTM! Clean imports and configuration.

The imports follow project conventions and the dayOptions array correctly includes all day values mentioned in the PR (1, 3, 7, 14, 30 days).


138-174: LGTM! Proper dialog structure and loading states.

The dialog implementation follows best practices:

  • Proper accessibility with Label and htmlFor
  • LoadingContent wrapper with skeleton for loading state
  • Toggle disabled during execution to prevent race conditions at the UI level
  • Clean separation of concerns

176-234: LGTM! Well-structured select controls with good UX.

Both select controls follow a consistent pattern:

  • Properly disabled based on enabled state and isExecuting flag
  • Display the selected value correctly with a fallback
  • Include helpful description text for users
  • Map options cleanly from the dayOptions array

The defensive fallback to "Select..." is good practice even though it shouldn't occur with the current data defaults.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
apps/web/utils/webhook/process-history-item.ts (1)

167-180: Well-structured follow-up label cleanup with proper error handling.

The implementation correctly clears follow-up labels after processing inbound messages. The try-catch prevents errors from propagating, and the use of captureException ensures issues are tracked in Sentry.

🛡️ Optional: Add defensive guard clause for threadId

While actualThreadId should always be defined in practice (email providers always supply threadIds), adding an explicit guard would make the code more defensive:

    }
 
+   // Guard: Only attempt to clear follow-up if we have a thread ID
+   if (!actualThreadId) {
+     logger.warn("No threadId available for follow-up cleanup");
+     return;
+   }
+
    // Remove follow-up label if present (they replied, so follow-up no longer needed)
    // This handles the case where we were awaiting a reply from them
    try {

This prevents unnecessary API calls and potential type issues if the threadId is somehow missing.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 446f9f1 and bf7b06f.

📒 Files selected for processing (3)
  • apps/web/utils/follow-up/cleanup.ts
  • apps/web/utils/reply-tracker/handle-outbound.ts
  • apps/web/utils/webhook/process-history-item.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/web/utils/reply-tracker/handle-outbound.ts
  • apps/web/utils/follow-up/cleanup.ts
🧰 Additional context used
📓 Path-based instructions (13)
**/*.{ts,tsx}

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

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

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

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

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

Files:

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

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

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

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

Files:

  • apps/web/utils/webhook/process-history-item.ts
apps/web/**/*.{ts,tsx}

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

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

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

Files:

  • apps/web/utils/webhook/process-history-item.ts
**/*.ts

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

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

Files:

  • apps/web/utils/webhook/process-history-item.ts
**/*.{tsx,ts}

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

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

Files:

  • apps/web/utils/webhook/process-history-item.ts
**/*.{tsx,ts,css}

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

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

Files:

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

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

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

Files:

  • apps/web/utils/webhook/process-history-item.ts
!(pages/_document).{jsx,tsx}

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

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

Files:

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

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

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

Files:

  • apps/web/utils/webhook/process-history-item.ts
**/{utils,helpers,lib}/**/*.{ts,tsx}

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

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

Files:

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

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

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

Files:

  • apps/web/utils/webhook/process-history-item.ts
apps/web/**/*.{ts,tsx,js,jsx,json,css}

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

Format code with Prettier

Files:

  • apps/web/utils/webhook/process-history-item.ts
apps/web/**/*.{example,ts,json}

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

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

Files:

  • apps/web/utils/webhook/process-history-item.ts
🧠 Learnings (13)
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail label operations from @/utils/gmail/label.ts instead of direct API calls

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • apps/web/utils/webhook/process-history-item.ts
📚 Learning: 2025-11-25T14:37:56.430Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Prefer using existing helpers from `@/__tests__/helpers.ts` (`getEmailAccount`, `getEmail`, `getRule`, `getMockMessage`, `getMockExecutedRule`) instead of creating custom test data helpers

Applied to files:

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

Applied to files:

  • apps/web/utils/webhook/process-history-item.ts
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/{pages,routes,components}/**/*.{ts,tsx} : Never call Gmail API directly from routes or components - always use wrapper functions from the utils folder

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • apps/web/utils/webhook/process-history-item.ts
🧬 Code graph analysis (1)
apps/web/utils/webhook/process-history-item.ts (2)
apps/web/utils/follow-up/cleanup.ts (1)
  • clearFollowUpLabel (103-143)
apps/web/utils/error.ts (1)
  • captureException (46-76)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test
🔇 Additional comments (1)
apps/web/utils/webhook/process-history-item.ts (1)

7-7: LGTM!

The new imports follow the project's path alias conventions and are used correctly in the follow-up label cleanup logic below.

Also applies to: 16-16

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx (1)

47-48: Consider guarding the action binding against missing data.id.

The action is bound with data?.id ?? "" which creates a binding with an empty string when data hasn't loaded. While the handlers correctly guard with if (!data) return;, the binding itself could theoretically be invoked with an invalid emailAccountId.

This is currently safe due to the early return at line 148, but consider using a more defensive pattern:

♻️ Suggested improvement
   const { execute, isExecuting } = useAction(
-    updateFollowUpSettingsAction.bind(null, data?.id ?? ""),
+    updateFollowUpSettingsAction.bind(null, data?.id || ""),
     {

Alternatively, you could conditionally create the action only when data exists, though the current implementation is functionally safe.

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

756-757: Consider adding a composite index for follow-up processing queries.

The process.ts file queries ThreadTracker with:

WHERE emailAccountId = ? AND type = ? AND resolved = false 
  AND followUpAppliedAt IS NULL AND sentAt < ?

The existing indexes cover (emailAccountId, type, resolved, sentAt) but not followUpAppliedAt. For better query performance as data grows, consider adding a composite index:

💡 Suggested index
   @@index([emailAccountId, resolved])
   @@index([emailAccountId, resolved, sentAt, type])
   @@index([emailAccountId, type, resolved, sentAt])
+  @@index([emailAccountId, type, resolved, followUpAppliedAt, sentAt])
 }

This is optional for now but may become important at scale.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bf7b06f and 00f4306.

📒 Files selected for processing (7)
  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/api/user/email-account/route.ts
  • apps/web/prisma/migrations/20260108121515_add_follow_up_auto_draft_setting/migration.sql
  • apps/web/prisma/schema.prisma
  • apps/web/utils/actions/follow-up-reminders.ts
  • apps/web/utils/actions/follow-up-reminders.validation.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/web/utils/actions/follow-up-reminders.validation.ts
  • apps/web/utils/actions/follow-up-reminders.ts
🧰 Additional context used
📓 Path-based instructions (25)
**/*.{ts,tsx}

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

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

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

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

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

Files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
  • apps/web/app/api/user/email-account/route.ts
**/*.{ts,tsx,js,jsx}

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

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

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

Files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
  • apps/web/app/api/user/email-account/route.ts
apps/web/**/*.{ts,tsx}

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

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

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

Files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
  • apps/web/app/api/user/email-account/route.ts
apps/web/app/api/**/*.{ts,tsx}

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

apps/web/app/api/**/*.{ts,tsx}: API routes must use withAuth, withEmailAccount, or withError middleware for authentication
All database queries must include user scoping with emailAccountId or userId filtering in WHERE clauses
Request parameters must be validated before use; avoid direct parameter usage without type checking
Use generic error messages instead of revealing internal details; throw SafeError instead of exposing user IDs, resource IDs, or system information
API routes should only return necessary fields using select in database queries to prevent unintended information disclosure
Cron endpoints must use hasCronSecret or hasPostCronSecret to validate cron requests and prevent unauthorized access
Request bodies should use Zod schemas for validation to ensure type safety and prevent injection attacks

Files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/api/user/email-account/route.ts
**/app/api/**/*.ts

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

**/app/api/**/*.ts: ALL API routes that handle user data MUST use appropriate middleware: use withEmailAccount for email-scoped operations, use withAuth for user-scoped operations, or use withError with proper validation for public/custom auth endpoints
Use withEmailAccount middleware for operations scoped to a specific email account, including reading/writing emails, rules, schedules, or any operation using emailAccountId
Use withAuth middleware for user-level operations such as user settings, API keys, and referrals that use only userId
Use withError middleware only for public endpoints, custom authentication logic, or cron endpoints. For cron endpoints, MUST use hasCronSecret() or hasPostCronSecret() validation
Cron endpoints without proper authentication can be triggered by anyone. CRITICAL: All cron endpoints MUST validate cron secret using hasCronSecret(request) or hasPostCronSecret(request) and capture unauthorized attempts with captureException()
Always validate request bodies using Zod schemas to ensure type safety and prevent invalid data from reaching database operations
Maintain consistent error response format across all API routes to avoid information disclosure while providing meaningful error feedback

Files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/api/user/email-account/route.ts
**/*.ts

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

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

Files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/api/user/email-account/route.ts
**/*.{tsx,ts}

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

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

Files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
  • apps/web/app/api/user/email-account/route.ts
**/*.{tsx,ts,css}

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

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

Files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
  • apps/web/app/api/user/email-account/route.ts
**/*.{js,jsx,ts,tsx}

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

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

Files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
  • apps/web/app/api/user/email-account/route.ts
!(pages/_document).{jsx,tsx}

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

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

Files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
  • apps/web/app/api/user/email-account/route.ts
  • apps/web/prisma/schema.prisma
  • apps/web/prisma/migrations/20260108121515_add_follow_up_auto_draft_setting/migration.sql
**/*.{js,ts,jsx,tsx}

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

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

Files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
  • apps/web/app/api/user/email-account/route.ts
apps/web/**/*.{ts,tsx,js,jsx}

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

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

Files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
  • apps/web/app/api/user/email-account/route.ts
apps/web/app/**/*.{ts,tsx}

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

Follow NextJS app router structure with (app) directory

Files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
  • apps/web/app/api/user/email-account/route.ts
apps/web/**/*.{ts,tsx,js,jsx,json,css}

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

Format code with Prettier

Files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
  • apps/web/app/api/user/email-account/route.ts
apps/web/**/*.{example,ts,json}

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

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

Files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/api/user/email-account/route.ts
apps/web/app/api/**/*.ts

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

apps/web/app/api/**/*.ts: Create GET API routes wrapped with withAuth or withEmailAccount middleware for fetching data
Export response types from GET API routes using export type GetXResponse = Awaited<ReturnType<typeof getData>>

Files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/api/user/email-account/route.ts
apps/web/app/(app)/**/*.{ts,tsx}

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

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

Files:

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

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

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
**/*.{jsx,tsx}

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

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
apps/web/**/*.{tsx,jsx}

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

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
apps/web/app/api/**/route.ts

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

apps/web/app/api/**/route.ts: Create GET API routes using withAuth or withEmailAccount middleware in apps/web/app/api/*/route.ts, export response types as GetExampleResponse type alias for client-side type safety
Always export response types from GET routes as Get[Feature]Response using type inference from the data fetching function for type-safe client consumption
Do NOT use POST API routes for mutations - always use server actions with next-safe-action instead

Files:

  • apps/web/app/api/user/email-account/route.ts
**/app/**/route.ts

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

**/app/**/route.ts: Always wrap GET API route handlers with withAuth or withEmailAccount middleware for consistent error handling and authentication in Next.js App Router
Infer and export response type for GET API routes using Awaited<ReturnType<typeof functionName>> pattern in Next.js
Use Prisma for database queries in GET API routes
Return responses using NextResponse.json() in GET API routes
Do not use try/catch blocks in GET API route handlers when using withAuth or withEmailAccount middleware, as the middleware handles error handling

Files:

  • apps/web/app/api/user/email-account/route.ts
apps/web/app/**/[!.]*/route.{ts,tsx}

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

Use kebab-case for route directories in Next.js App Router (e.g., api/hello-world/route)

Files:

  • apps/web/app/api/user/email-account/route.ts
**/{app,pages}/**/{route,+page}.{ts,tsx}

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

**/{app,pages}/**/{route,+page}.{ts,tsx}: Use middleware wrappers (withError, withAuth, withEmailAccount, withEmailProvider) that automatically create loggers with request context in API routes
Enrich logger context within route handlers using logger.with() to add request-specific fields like messageId

Files:

  • apps/web/app/api/user/email-account/route.ts
**/prisma/schema.prisma

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

Use PostgreSQL as the database system with Prisma

Files:

  • apps/web/prisma/schema.prisma
🧠 Learnings (41)
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
  • apps/web/app/api/user/email-account/route.ts
📚 Learning: 2025-11-25T14:39:08.150Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-11-25T14:39:08.150Z
Learning: Applies to apps/web/app/api/**/*.{ts,tsx} : All database queries must include user scoping with `emailAccountId` or `userId` filtering in WHERE clauses

Applied to files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
  • apps/web/app/api/user/email-account/route.ts
📚 Learning: 2025-11-25T14:39:04.892Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-11-25T14:39:04.892Z
Learning: Applies to apps/web/app/api/**/route.ts : All database queries must include user/account filtering with `emailAccountId` or `userId` in WHERE clauses to prevent IDOR vulnerabilities

Applied to files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
  • apps/web/app/api/user/email-account/route.ts
📚 Learning: 2025-07-08T13:14:07.449Z
Learnt from: elie222
Repo: elie222/inbox-zero 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.

Applied to files:

  • apps/web/app/api/follow-up-reminders/process.ts
📚 Learning: 2025-11-25T14:39:23.326Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:23.326Z
Learning: Applies to app/api/**/*.ts : ALL API routes that handle user data MUST use appropriate middleware: `withEmailAccount` for email-scoped operations, `withAuth` for user-scoped operations, or `withError` with proper validation for public/cron endpoints

Applied to files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/api/user/email-account/route.ts
📚 Learning: 2025-11-25T14:39:49.448Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-11-25T14:39:49.448Z
Learning: Applies to apps/web/utils/actions/*.ts : Use `actionClient` when both authenticated user context and a specific emailAccountId are needed, with emailAccountId bound when calling from the client

Applied to files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:39:23.326Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:23.326Z
Learning: Applies to **/*.ts : ALL database queries MUST be scoped to the authenticated user/account - include user/account filtering in `where` clauses (e.g., `emailAccountId`, `userId`) to ensure users only access their own resources

Applied to files:

  • apps/web/app/api/follow-up-reminders/process.ts
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : Implement early returns for invalid LLM inputs, use proper error types and logging, implement fallbacks for AI failures, and add retry logic for transient failures using `withRetry`

Applied to files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/utils/actions/**/*.ts : Use proper error handling with try/catch blocks

Applied to files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:37:22.822Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/get-api-route.mdc:0-0
Timestamp: 2025-11-25T14:37:22.822Z
Learning: Applies to **/app/**/route.ts : Do not use try/catch blocks in GET API route handlers when using `withAuth` or `withEmailAccount` middleware, as the middleware handles error handling

Applied to files:

  • apps/web/app/api/follow-up-reminders/process.ts
📚 Learning: 2025-12-31T23:49:09.597Z
Learnt from: rsnodgrass
Repo: elie222/inbox-zero PR: 1154
File: apps/web/app/api/user/setup-progress/route.ts:0-0
Timestamp: 2025-12-31T23:49:09.597Z
Learning: In apps/web/app/api/user/setup-progress/route.ts, Reply Zero enabled status should be determined solely by checking if the TO_REPLY rule is enabled, as it is the critical/canonical rule that Reply Zero is based on. The other conversation status types (FYI, AWAITING_REPLY, ACTIONED) should not be checked for determining Reply Zero setup progress.
<!-- </add_learning>

Applied to files:

  • apps/web/app/api/follow-up-reminders/process.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2026-01-07T21:07:34.062Z
Learnt from: elie222
Repo: elie222/inbox-zero PR: 1230
File: apps/web/utils/ai/document-filing/parse-filing-reply.ts:1-4
Timestamp: 2026-01-07T21:07:34.062Z
Learning: In apps/web/utils/ai/**/*.ts files, when using createGenerateObject with a label parameter, a separate scoped logger is not needed because createGenerateObject handles logging internally via the label. Adding a scoped logger in these cases would duplicate logs.

Applied to files:

  • apps/web/app/api/follow-up-reminders/process.ts
📚 Learning: 2025-11-25T14:36:36.276Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-11-25T14:36:36.276Z
Learning: Applies to **/*.{ts,tsx} : Use `result?.serverError` with `toastError` and `toastSuccess` for error handling in server actions

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/**/*.{tsx,jsx} : Use `result?.serverError` with `toastError` and `toastSuccess` for error handling in forms

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:36:40.146Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-11-25T14:36:40.146Z
Learning: Applies to **/*.{ts,tsx} : Use `result?.serverError` with `toastError` from `@/components/Toast` for error handling in async operations

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't use optional chaining where undefined values aren't allowed

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:39:23.326Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:23.326Z
Learning: Applies to app/api/**/*.ts : Use `SafeError` for error responses to prevent information disclosure - provide generic messages (e.g., 'Rule not found' not 'Rule {id} does not exist for user {userId}') without revealing internal IDs or ownership details

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:39:27.909Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:27.909Z
Learning: Applies to **/*.ts : Use SafeError for error responses to prevent information disclosure. Generic error messages should not reveal internal IDs, logic, or resource ownership details

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2026-01-07T21:07:06.691Z
Learnt from: elie222
Repo: elie222/inbox-zero PR: 1230
File: apps/web/app/(app)/[emailAccountId]/drive/page.tsx:47-70
Timestamp: 2026-01-07T21:07:06.691Z
Learning: In TSX files across the codebase, prefer direct server action calls with manual error handling (e.g., check result?.serverError and handle errors explicitly). The useAction hook pattern is not required; you can call server actions directly and use try/finally to ensure cleanup (such as resetting loading states) regardless of the hook. Apply consistently for components that perform server interactions.

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Make sure to pass a message value when creating a built-in error

Applied to files:

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

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:37:30.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/hooks.mdc:0-0
Timestamp: 2025-11-25T14:37:30.660Z
Learning: Custom hooks should encapsulate reusable stateful logic, especially for data fetching or complex UI interactions

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:37:09.306Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-11-25T14:37:09.306Z
Learning: Applies to apps/web/utils/actions/*.ts : Server actions should use 'use server' directive and automatically receive authentication context (`emailAccountId`) from the `actionClient`

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:36:36.276Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-11-25T14:36:36.276Z
Learning: For mutating data, use Next.js server actions instead of SWR

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/hooks/**/*.{ts,tsx} : Call `mutate()` after successful mutations in SWR hooks to refresh data

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:37:09.306Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-11-25T14:37:09.306Z
Learning: Applies to apps/web/hooks/use*.ts : Call `mutate()` on SWR hooks after successful mutations to refresh cached data

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:36:40.146Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-11-25T14:36:40.146Z
Learning: Applies to **/*{.action,.server}.{ts,tsx} : For mutating data, use Next.js server actions

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:39:27.909Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:27.909Z
Learning: Applies to **/app/api/**/*.ts : ALL API routes that handle user data MUST use appropriate middleware: use `withEmailAccount` for email-scoped operations, use `withAuth` for user-scoped operations, or use `withError` with proper validation for public/custom auth endpoints

Applied to files:

  • apps/web/app/api/user/email-account/route.ts
📚 Learning: 2025-11-25T14:39:04.892Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-11-25T14:39:04.892Z
Learning: Applies to apps/web/app/api/**/route.ts : API responses should use `select` to return only necessary fields and avoid exposing sensitive data

Applied to files:

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

Applied to files:

  • apps/web/app/api/user/email-account/route.ts
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/app/api/**/*.ts : Create GET API routes wrapped with `withAuth` or `withEmailAccount` middleware for fetching data

Applied to files:

  • apps/web/app/api/user/email-account/route.ts
📚 Learning: 2025-11-25T14:39:08.150Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security-audit.mdc:0-0
Timestamp: 2025-11-25T14:39:08.150Z
Learning: Applies to apps/web/app/api/**/*.{ts,tsx} : API routes should only return necessary fields using `select` in database queries to prevent unintended information disclosure

Applied to files:

  • apps/web/app/api/user/email-account/route.ts
🧬 Code graph analysis (1)
apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx (7)
apps/web/utils/actions/follow-up-reminders.ts (1)
  • updateFollowUpSettingsAction (7-32)
apps/web/components/Toast.tsx (2)
  • toastSuccess (3-12)
  • toastError (14-19)
apps/web/components/SettingCard.tsx (1)
  • SettingCard (3-34)
apps/web/components/LoadingContent.tsx (1)
  • LoadingContent (13-31)
apps/web/components/Input.tsx (1)
  • Label (116-132)
apps/web/components/Toggle.tsx (1)
  • Toggle (18-50)
apps/web/components/ui/select.tsx (4)
  • Select (150-150)
  • SelectTrigger (153-153)
  • SelectContent (154-154)
  • SelectItem (156-156)
⏰ 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: cubic · AI code reviewer
  • GitHub Check: test
🔇 Additional comments (10)
apps/web/prisma/migrations/20260108121515_add_follow_up_auto_draft_setting/migration.sql (1)

1-2: LGTM!

The migration correctly adds the followUpAutoDraftEnabled boolean column with a sensible default of true, ensuring existing accounts automatically have the feature enabled. This aligns with the Prisma schema definition.

apps/web/app/api/user/email-account/route.ts (1)

27-30: LGTM!

The new follow-up settings fields are correctly added to the select clause. The route properly uses withEmailAccount middleware for authentication, scopes the query to emailAccountId, uses SafeError for error handling, and exports the response type following the Get[Feature]Response pattern. Based on learnings and coding guidelines.

apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx (3)

37-63: Well-structured hook usage and error handling.

The component correctly:

  • Defines all hooks before conditional returns (React hooks rules)
  • Uses useAction with proper onSuccess/onError callbacks
  • Implements toast notifications following the coding guidelines (toastError with serverError)
  • Calls mutate() after mutations to refresh SWR cache

65-145: Optimistic updates implemented correctly.

Each handler properly:

  1. Guards against missing data
  2. Creates optimistic data and calls mutate(optimisticData, false) to update UI immediately
  3. Executes the server action
  4. Reverts on error via the onError callback's mutate() call

200-216: Good UX: Auto-draft toggle disabled when reminders are off.

The auto-draft toggle is correctly disabled when !enabled, preventing users from configuring a sub-feature when the parent feature is disabled.

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

148-152: LGTM!

The new follow-up settings fields are well-designed:

  • followUpRemindersEnabled defaults to false (opt-in feature)
  • Day thresholds default to 3 days (reasonable default)
  • followUpAutoDraftEnabled defaults to true (expected behavior when enabling reminders)
apps/web/app/api/follow-up-reminders/process.ts (4)

12-71: LGTM! Well-structured batch processing with proper error isolation.

The function correctly:

  • Filters accounts by followUpRemindersEnabled AND premium status
  • Uses select to fetch only necessary fields (per coding guidelines)
  • Isolates per-account errors with try/catch and captureException
  • Continues processing other accounts on failure
  • Returns useful summary metrics

94-104: Good defensive check for missing provider.

The early return when account.provider is null prevents runtime errors and logs the skip appropriately.


213-223: LGTM! Non-critical cleanup properly isolated.

The stale drafts cleanup is correctly wrapped in its own try/catch since it's a maintenance task that shouldn't fail the overall processing.


151-182: No action needed—applyFollowUpLabel is already idempotent.

The function is safe to call multiple times on the same message. Gmail's API inherently handles duplicate label additions as no-ops, and Outlook's implementation explicitly checks for existing labels before applying. If generateFollowUpDraft fails and the tracker is retried, reapplying the label will not cause issues.

Likely an incorrect or invalid review comment.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
.claude/commands/write-tests.md (1)

49-55: Step 0 auto-detection command scope could be clearer.

The comment "Auto-detect: staged → branch diff → specified files" followed by a single git diff --cached command suggests a linear fallback strategy, but the implementation shows only one command. Consider clarifying the intended flow:

  • Does --cached represent staged changes?
  • Should the instructions show fallback commands for other scenarios (e.g., git diff main...HEAD for branch-relative changes)?

Adding a brief comment explaining when each command applies would improve clarity for users unfamiliar with git diff semantics.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4194b84 and 5283726.

📒 Files selected for processing (1)
  • .claude/commands/write-tests.md
🧰 Additional context used
📓 Path-based instructions (1)
!(pages/_document).{jsx,tsx}

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

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

Files:

  • .claude/commands/write-tests.md
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test
🔇 Additional comments (1)
.claude/commands/write-tests.md (1)

100-100: Clarify the "Do NOT use sandbox" constraint.

Line 100 states "Do NOT use sandbox for test commands" without explanation. This instruction stands out as potentially context-dependent or stale. Please clarify:

  • Why should the sandbox be avoided?
  • What is the impact if someone uses it (e.g., does pnpm test fail in sandbox)?
  • Is this a known limitation of the testing environment?

This will help future readers understand the constraint and avoid confusion.

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

🤖 Fix all issues with AI agents
In @apps/web/prisma/schema.prisma:
- Around line 757-758: Add a Prisma index that includes followUpAppliedAt to
speed up the follow-up queries: add a composite index covering emailAccountId,
type, resolved, followUpAppliedAt, sentAt to the model that defines the
followUpAppliedAt field (or alternatively add two indexes: one on
emailAccountId+type+resolved+followUpAppliedAt and a second on
emailAccountId+followUpAppliedAt) so the processor queries using WHERE
followUpAppliedAt IS NULL and the cleanup query using WHERE followUpAppliedAt <
threshold will use an index.
🧹 Nitpick comments (1)
apps/web/prisma/schema.prisma (1)

150-151: Consider more explicit field naming.

The fields followUpAwaitingReplyDays and followUpNeedsReplyDays could be more explicit about their purpose. Consider names like followUpAwaitingReplyReminderDays and followUpNeedsReplyReminderDays to clarify that these represent the delay before a reminder is triggered, not just a general time period.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5283726 and e7bcfbb.

📒 Files selected for processing (3)
  • .claude/commands/write-tests.md
  • apps/web/prisma/schema.prisma
  • apps/web/utils/webhook/process-history-item.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • .claude/commands/write-tests.md
  • apps/web/utils/webhook/process-history-item.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/prisma/schema.prisma

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

Use PostgreSQL as the database system with Prisma

Files:

  • apps/web/prisma/schema.prisma
!(pages/_document).{jsx,tsx}

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

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

Files:

  • apps/web/prisma/schema.prisma
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test
🔇 Additional comments (1)
apps/web/prisma/schema.prisma (1)

148-153: Application-level validation is already properly enforced.

The followUpAwaitingReplyDays and followUpNeedsReplyDays fields are validated in apps/web/utils/actions/follow-up-reminders.validation.ts with Zod constraints (z.number().int().min(1).max(30)), which ensures only positive integers between 1 and 30 are accepted. Database-level CHECK constraints are not necessary given the existing application-level validation pattern used throughout the codebase.

Likely an incorrect or invalid review comment.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

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

55-59: Use getActionErrorMessage utility for error extraction.

Per coding guidelines, error messages should be extracted using the centralized utility rather than accessing serverError directly.

♻️ Suggested refactor

Add the import at the top of the file:

import { getActionErrorMessage } from "@/utils/error";

Then update the error handler:

       onError: (error) => {
         mutate();
         toastError({
-          description: error.error?.serverError ?? "Failed to update settings",
+          description: getActionErrorMessage(error.error, "Failed to update settings"),
         });
       },
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e7bcfbb and 720ae43.

📒 Files selected for processing (1)
  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
🧰 Additional context used
📓 Path-based instructions (17)
**/*.{ts,tsx}

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

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

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

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

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
apps/web/app/(app)/**/*.{ts,tsx}

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

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
**/*.{ts,tsx,js,jsx}

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

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

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
apps/web/**/*.{ts,tsx}

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

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

apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Do not export types/interfaces that are only used within the same file. Export later if needed
Infer types from Zod schemas using z.infer<typeof schema> instead of duplicating as separate interfaces

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
**/*.{tsx,ts}

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

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
**/*.{tsx,ts,css}

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

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

Files:

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

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

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
**/*.{js,jsx,ts,tsx}

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

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
**/*.{jsx,tsx}

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

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
!(pages/_document).{jsx,tsx}

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

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
**/*.{js,ts,jsx,tsx}

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

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

**/*.{js,ts,jsx,tsx}: Add helper functions to the bottom of files, not the top!
All imports go at the top of files, no mid-file dynamic imports.

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
apps/web/**/*.{ts,tsx,js,jsx}

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

apps/web/**/*.{ts,tsx,js,jsx}: Use @/ path aliases for imports from project root
Use proper error handling with try/catch blocks
Prefer self-documenting code over comments; use descriptive variable and function names instead of explaining intent with comments. Never add comments that just describe what the code does. Only add comments for 'why' not 'what'
Add helper functions to the bottom of files, not the top
All imports go at the top of files, no mid-file dynamic imports
Use getActionErrorMessage(error.error) from @/utils/error to extract user-friendly error messages

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
apps/web/**/*.{ts,tsx,css}

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

Follow tailwindcss patterns with prettier-plugin-tailwindcss

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
apps/web/**/*.{tsx,jsx}

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

apps/web/**/*.{tsx,jsx}: Prefer functional components with hooks
Use shadcn/ui components when available
Follow consistent naming conventions (PascalCase for components)
Use LoadingContent component for async data with loading and error states
Use React Hook Form with Zod validation for form handling
Use useAction hook from next-safe-action/hooks with onSuccess and onError callbacks for form submission
Use LoadingContent component to handle loading and error states consistently
Call mutate() after successful mutations to refresh SWR data

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
apps/web/**/*.{tsx,jsx,css}

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

Ensure responsive design with mobile-first approach

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
apps/web/**/*.{ts,tsx,js,jsx,json}

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

Client-side environment variables must be prefixed with NEXT_PUBLIC_

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
**/*.{js,ts,jsx,tsx,py,java,cs,rb,go,php,rs,kt,swift,m}

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

Prefer self-documenting code over comments; use descriptive variable and function names instead of explaining intent with comments. Never add comments that just describe what the code does - code should explain itself. Only add comments for 'why' not 'what'.

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
🧠 Learnings (29)
📚 Learning: 2025-11-25T14:36:36.276Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-11-25T14:36:36.276Z
Learning: Applies to **/*.{ts,tsx} : Use `result?.serverError` with `toastError` and `toastSuccess` for error handling in server actions

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:36:40.146Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-11-25T14:36:40.146Z
Learning: Applies to **/*.{ts,tsx} : Use `result?.serverError` with `toastError` from `@/components/Toast` for error handling in async operations

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2026-01-08T15:09:06.736Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2026-01-08T15:09:06.736Z
Learning: Applies to apps/web/components/**/*.tsx : Use `getActionErrorMessage(error.error)` utility to extract user-friendly error messages from server actions, supporting optional `prefix` parameter

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2026-01-09T21:51:15.182Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2026-01-09T21:51:15.182Z
Learning: Applies to apps/web/**/*.{ts,tsx,js,jsx} : Use `getActionErrorMessage(error.error)` from `@/utils/error` to extract user-friendly error messages

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2026-01-09T17:27:19.225Z
Learnt from: elie222
Repo: elie222/inbox-zero PR: 1234
File: apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx:65-83
Timestamp: 2026-01-09T17:27:19.225Z
Learning: In the elie222/inbox-zero repo, for React components using next-safe-action, using optimistic updates with mutate on success/error is an accepted approach to address race conditions in toggle handlers. It is acceptable for components not to guard rapid toggles with isExecuting (disable) when the optimistic UI state is reconciled by mutate. Apply this guidance to similar TSX components in the web app where appropriate.

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't use optional chaining where undefined values aren't allowed

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2026-01-09T21:51:15.182Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2026-01-09T21:51:15.182Z
Learning: Applies to apps/web/**/*.{ts,tsx,js,jsx} : Use proper error handling with try/catch blocks

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:39:23.326Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:23.326Z
Learning: Applies to app/api/**/*.ts : Use `SafeError` for error responses to prevent information disclosure - provide generic messages (e.g., 'Rule not found' not 'Rule {id} does not exist for user {userId}') without revealing internal IDs or ownership details

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:39:27.909Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:27.909Z
Learning: Applies to **/*.ts : Use SafeError for error responses to prevent information disclosure. Generic error messages should not reveal internal IDs, logic, or resource ownership details

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : Implement early returns for invalid LLM inputs, use proper error types and logging, implement fallbacks for AI failures, and add retry logic for transient failures using `withRetry`

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2026-01-07T21:07:06.691Z
Learnt from: elie222
Repo: elie222/inbox-zero PR: 1230
File: apps/web/app/(app)/[emailAccountId]/drive/page.tsx:47-70
Timestamp: 2026-01-07T21:07:06.691Z
Learning: In TSX files across the codebase, prefer direct server action calls with manual error handling (e.g., check result?.serverError and handle errors explicitly). The useAction hook pattern is not required; you can call server actions directly and use try/finally to ensure cleanup (such as resetting loading states) regardless of the hook. Apply consistently for components that perform server interactions.

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2026-01-09T21:51:15.182Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2026-01-09T21:51:15.182Z
Learning: Applies to apps/web/**/*.{tsx,jsx} : Use LoadingContent component for async data with loading and error states

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:37:30.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/hooks.mdc:0-0
Timestamp: 2025-11-25T14:37:30.660Z
Learning: Custom hooks should encapsulate reusable stateful logic, especially for data fetching or complex UI interactions

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2026-01-08T15:09:06.736Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2026-01-08T15:09:06.736Z
Learning: Applies to apps/web/utils/actions/!(*.validation).ts : Use `next-safe-action` for all server actions with `actionClient`, `.metadata()`, `.inputSchema()`, and `.action()` chain pattern

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2026-01-08T15:09:06.736Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2026-01-08T15:09:06.736Z
Learning: Applies to apps/web/components/**/*.tsx : Use `useAction` hook with `onSuccess` and `onError` callbacks for handling server action responses in forms

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:39:49.448Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-11-25T14:39:49.448Z
Learning: Applies to apps/web/utils/actions/*.ts : Use `actionClient` when both authenticated user context and a specific emailAccountId are needed, with emailAccountId bound when calling from the client

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-12-31T23:49:09.597Z
Learnt from: rsnodgrass
Repo: elie222/inbox-zero PR: 1154
File: apps/web/app/api/user/setup-progress/route.ts:0-0
Timestamp: 2025-12-31T23:49:09.597Z
Learning: In apps/web/app/api/user/setup-progress/route.ts, Reply Zero enabled status should be determined solely by checking if the TO_REPLY rule is enabled, as it is the critical/canonical rule that Reply Zero is based on. The other conversation status types (FYI, AWAITING_REPLY, ACTIONED) should not be checked for determining Reply Zero setup progress.
<!-- </add_learning>

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2026-01-09T21:51:15.182Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2026-01-09T21:51:15.182Z
Learning: Applies to apps/web/utils/actions/*.ts : Use `next-safe-action` with proper Zod validation for server actions

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2026-01-08T15:09:06.736Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2026-01-08T15:09:06.736Z
Learning: Applies to apps/web/(hooks|components)/**/*.ts?(x) : Call `mutate()` after successful mutations to refresh SWR data

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:36:36.276Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-11-25T14:36:36.276Z
Learning: For mutating data, use Next.js server actions instead of SWR

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2026-01-09T21:51:15.182Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2026-01-09T21:51:15.182Z
Learning: Applies to apps/web/**/*.{tsx,jsx} : Call `mutate()` after successful mutations to refresh SWR data

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:36:40.146Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/data-fetching.mdc:0-0
Timestamp: 2025-11-25T14:36:40.146Z
Learning: Applies to **/*{.action,.server}.{ts,tsx} : For mutating data, use Next.js server actions

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2026-01-09T21:51:15.182Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2026-01-09T21:51:15.182Z
Learning: Applies to apps/web/utils/actions/*.ts : Call `revalidatePath` in server actions after successful mutations

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:39:49.448Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-11-25T14:39:49.448Z
Learning: Applies to apps/web/utils/actions/*.ts : Implement all server actions using the `next-safe-action` library with actionClient, actionClientUser, or adminActionClient for type safety and validation

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2025-11-25T14:39:49.448Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/server-actions.mdc:0-0
Timestamp: 2025-11-25T14:39:49.448Z
Learning: Applies to apps/web/utils/actions/*.ts : Use `actionClientUser` when only authenticated user context (userId) is needed

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
📚 Learning: 2026-01-09T21:51:15.182Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2026-01-09T21:51:15.182Z
Learning: Applies to apps/web/utils/actions/*.ts : Server actions should use `actionClient.metadata({ name: 'actionName' }).schema(schema).action(...)` pattern

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx
🧬 Code graph analysis (1)
apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx (8)
apps/web/utils/actions/follow-up-reminders.ts (1)
  • updateFollowUpSettingsAction (7-32)
apps/web/components/Toast.tsx (2)
  • toastSuccess (3-12)
  • toastError (14-19)
apps/web/components/SettingCard.tsx (1)
  • SettingCard (3-34)
apps/web/components/Toggle.tsx (1)
  • Toggle (18-50)
apps/web/components/ui/dialog.tsx (6)
  • Dialog (116-116)
  • DialogTrigger (120-120)
  • DialogContent (121-121)
  • DialogHeader (122-122)
  • DialogTitle (124-124)
  • DialogDescription (125-125)
apps/web/components/LoadingContent.tsx (1)
  • LoadingContent (13-31)
apps/web/components/Input.tsx (1)
  • Label (116-132)
apps/web/components/ui/select.tsx (4)
  • Select (150-150)
  • SelectTrigger (153-153)
  • SelectContent (154-154)
  • SelectItem (156-156)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test
🔇 Additional comments (3)
apps/web/app/(app)/[emailAccountId]/assistant/settings/FollowUpRemindersSetting.tsx (3)

64-82: LGTM — Optimistic update handlers are well-implemented.

All four handlers follow a consistent pattern: guard against missing data, apply optimistic update via mutate(optimisticData, false), then execute the server action. The dependency arrays are correctly specified. Based on learnings, this optimistic approach with mutate reconciliation is an accepted pattern in this codebase.

Also applies to: 84-103, 105-124, 126-144


146-162: LGTM — Early return provides graceful loading state.

Returning a disabled toggle when data is unavailable prevents actions from being called with undefined values and provides clear UX feedback.


164-280: LGTM — Component structure and Dialog implementation are well-organized.

The SettingCard with conditional Configure button, Dialog with form controls, and LoadingContent wrapper follow established patterns. The Select components properly use controlled values with handlers, and the Toggle component correctly receives disabled state during execution within the dialog.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
apps/web/utils/follow-up/generate-draft.ts (1)

62-62: Use at(-1) for accessing the last array element.

Per coding guidelines, prefer at() over integer index access for array elements.

♻️ Suggested change
-    const lastMessage = thread.messages[thread.messages.length - 1];
+    const lastMessage = thread.messages.at(-1);

Note: With this change, lastMessage becomes ParsedMessage | undefined, but since you've already validated thread.messages?.length is truthy, TypeScript may require a non-null assertion or guard. You could use:

const lastMessage = thread.messages.at(-1)!;
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 720ae43 and c68c77f.

📒 Files selected for processing (1)
  • apps/web/utils/follow-up/generate-draft.ts
🧰 Additional context used
📓 Path-based instructions (14)
**/*.{ts,tsx}

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

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

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

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

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

Files:

  • apps/web/utils/follow-up/generate-draft.ts
**/*.{ts,tsx,js,jsx}

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

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

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

Files:

  • apps/web/utils/follow-up/generate-draft.ts
apps/web/**/*.{ts,tsx}

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

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

apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Do not export types/interfaces that are only used within the same file. Export later if needed
Infer types from Zod schemas using z.infer<typeof schema> instead of duplicating as separate interfaces

Files:

  • apps/web/utils/follow-up/generate-draft.ts
**/*.ts

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

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

Files:

  • apps/web/utils/follow-up/generate-draft.ts
**/*.{tsx,ts}

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

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

Files:

  • apps/web/utils/follow-up/generate-draft.ts
**/*.{tsx,ts,css}

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

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

Files:

  • apps/web/utils/follow-up/generate-draft.ts
**/*.{js,jsx,ts,tsx}

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

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

Files:

  • apps/web/utils/follow-up/generate-draft.ts
!(pages/_document).{jsx,tsx}

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

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

Files:

  • apps/web/utils/follow-up/generate-draft.ts
**/*.{js,ts,jsx,tsx}

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

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

**/*.{js,ts,jsx,tsx}: Add helper functions to the bottom of files, not the top!
All imports go at the top of files, no mid-file dynamic imports.

Files:

  • apps/web/utils/follow-up/generate-draft.ts
**/{utils,helpers,lib}/**/*.{ts,tsx}

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

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

Files:

  • apps/web/utils/follow-up/generate-draft.ts
apps/web/**/*.{ts,tsx,js,jsx}

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

apps/web/**/*.{ts,tsx,js,jsx}: Use @/ path aliases for imports from project root
Use proper error handling with try/catch blocks
Prefer self-documenting code over comments; use descriptive variable and function names instead of explaining intent with comments. Never add comments that just describe what the code does. Only add comments for 'why' not 'what'
Add helper functions to the bottom of files, not the top
All imports go at the top of files, no mid-file dynamic imports
Use getActionErrorMessage(error.error) from @/utils/error to extract user-friendly error messages

Files:

  • apps/web/utils/follow-up/generate-draft.ts
apps/web/**/*.{ts,tsx,css}

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

Follow tailwindcss patterns with prettier-plugin-tailwindcss

Files:

  • apps/web/utils/follow-up/generate-draft.ts
apps/web/**/*.{ts,tsx,js,jsx,json}

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

Client-side environment variables must be prefixed with NEXT_PUBLIC_

Files:

  • apps/web/utils/follow-up/generate-draft.ts
**/*.{js,ts,jsx,tsx,py,java,cs,rb,go,php,rs,kt,swift,m}

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

Prefer self-documenting code over comments; use descriptive variable and function names instead of explaining intent with comments. Never add comments that just describe what the code does - code should explain itself. Only add comments for 'why' not 'what'.

Files:

  • apps/web/utils/follow-up/generate-draft.ts
🧠 Learnings (4)
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail thread operations from @/utils/gmail/thread.ts instead of direct API calls

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • apps/web/utils/follow-up/generate-draft.ts
🧬 Code graph analysis (1)
apps/web/utils/follow-up/generate-draft.ts (3)
apps/web/utils/email/types.ts (1)
  • EmailProvider (45-251)
apps/web/utils/logger.ts (1)
  • Logger (6-6)
apps/web/utils/reply-tracker/generate-draft.ts (1)
  • fetchMessagesAndGenerateDraft (29-79)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test
🔇 Additional comments (3)
apps/web/utils/follow-up/generate-draft.ts (3)

1-20: LGTM!

Imports follow project conventions - using centralized prisma, proper type imports, and logger passed as parameter per coding guidelines for helper functions.


64-87: LGTM!

Draft generation and creation logic is well-structured:

  • Correctly uses fetchMessagesAndGenerateDraft for LLM-based content generation
  • Properly interfaces with EmailProvider.draftEmail
  • Error handling logs context before rethrowing for upstream handling
  • Success logging includes actionable identifiers (threadId, draftId)

24-47: No type compatibility issues found. The Prisma query selects exactly the fields required by EmailAccountWithAI, including all nested user and account relations. The fields are structurally identical to the type definition.

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

🤖 Fix all issues with AI agents
In @apps/web/utils/webhook/process-history-item.ts:
- Around line 174-185: Add a guard to avoid calling clearFollowUpLabel with an
undefined thread id: check that actualThreadId (resolved from the function
parameter threadId or parsedMessage.threadId) is a non-empty string before
invoking clearFollowUpLabel; if it is falsy, skip the call (and the try/catch)
so you don't pass undefined into clearFollowUpLabel, and keep existing
logging/exception capture only around the actual call when actualThreadId is
present.
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c68c77f and af27a1d.

📒 Files selected for processing (2)
  • apps/web/prisma/schema.prisma
  • apps/web/utils/webhook/process-history-item.ts
🧰 Additional context used
📓 Path-based instructions (15)
**/*.{ts,tsx}

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

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

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

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

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

Files:

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

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

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

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

Files:

  • apps/web/utils/webhook/process-history-item.ts
apps/web/**/*.{ts,tsx}

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

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

apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Do not export types/interfaces that are only used within the same file. Export later if needed
Infer types from Zod schemas using z.infer<typeof schema> instead of duplicating as separate interfaces

Files:

  • apps/web/utils/webhook/process-history-item.ts
**/*.ts

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

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

Files:

  • apps/web/utils/webhook/process-history-item.ts
**/*.{tsx,ts}

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

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

Files:

  • apps/web/utils/webhook/process-history-item.ts
**/*.{tsx,ts,css}

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

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

Files:

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

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

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

Files:

  • apps/web/utils/webhook/process-history-item.ts
!(pages/_document).{jsx,tsx}

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

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

Files:

  • apps/web/utils/webhook/process-history-item.ts
  • apps/web/prisma/schema.prisma
**/*.{js,ts,jsx,tsx}

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

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

**/*.{js,ts,jsx,tsx}: Add helper functions to the bottom of files, not the top!
All imports go at the top of files, no mid-file dynamic imports.

Files:

  • apps/web/utils/webhook/process-history-item.ts
**/{utils,helpers,lib}/**/*.{ts,tsx}

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

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

Files:

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

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

apps/web/**/*.{ts,tsx,js,jsx}: Use @/ path aliases for imports from project root
Use proper error handling with try/catch blocks
Prefer self-documenting code over comments; use descriptive variable and function names instead of explaining intent with comments. Never add comments that just describe what the code does. Only add comments for 'why' not 'what'
Add helper functions to the bottom of files, not the top
All imports go at the top of files, no mid-file dynamic imports
Use getActionErrorMessage(error.error) from @/utils/error to extract user-friendly error messages

Files:

  • apps/web/utils/webhook/process-history-item.ts
apps/web/**/*.{ts,tsx,css}

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

Follow tailwindcss patterns with prettier-plugin-tailwindcss

Files:

  • apps/web/utils/webhook/process-history-item.ts
apps/web/**/*.{ts,tsx,js,jsx,json}

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

Client-side environment variables must be prefixed with NEXT_PUBLIC_

Files:

  • apps/web/utils/webhook/process-history-item.ts
**/*.{js,ts,jsx,tsx,py,java,cs,rb,go,php,rs,kt,swift,m}

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

Prefer self-documenting code over comments; use descriptive variable and function names instead of explaining intent with comments. Never add comments that just describe what the code does - code should explain itself. Only add comments for 'why' not 'what'.

Files:

  • apps/web/utils/webhook/process-history-item.ts
**/prisma/schema.prisma

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

Use PostgreSQL as the database system with Prisma

Files:

  • apps/web/prisma/schema.prisma
🧠 Learnings (14)
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail label operations from @/utils/gmail/label.ts instead of direct API calls

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • apps/web/utils/webhook/process-history-item.ts
📚 Learning: 2025-11-25T14:37:56.430Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Prefer using existing helpers from `@/__tests__/helpers.ts` (`getEmailAccount`, `getEmail`, `getRule`, `getMockMessage`, `getMockExecutedRule`) instead of creating custom test data helpers

Applied to files:

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

Applied to files:

  • apps/web/utils/webhook/process-history-item.ts
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/{pages,routes,components}/**/*.{ts,tsx} : Never call Gmail API directly from routes or components - always use wrapper functions from the utils folder

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • apps/web/utils/webhook/process-history-item.ts
📚 Learning: 2025-11-25T14:38:42.022Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/prisma.mdc:0-0
Timestamp: 2025-11-25T14:38:42.022Z
Learning: Applies to **/prisma/schema.prisma : Use PostgreSQL as the database system with Prisma

Applied to files:

  • apps/web/prisma/schema.prisma
🧬 Code graph analysis (1)
apps/web/utils/webhook/process-history-item.ts (2)
apps/web/utils/follow-up/cleanup.ts (1)
  • clearFollowUpLabel (103-143)
apps/web/utils/error.ts (1)
  • captureException (47-77)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test
🔇 Additional comments (4)
apps/web/utils/webhook/process-history-item.ts (1)

7-7: LGTM!

The imports are correctly placed and both utilities are properly used in the follow-up label cleanup logic.

Also applies to: 16-16

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

148-152: LGTM! Consider user expectations for auto-draft default.

The follow-up reminder fields are well-designed with appropriate types and defaults. The followUpAutoDraftEnabled field defaults to true, which means auto-draft generation will be enabled by default when users enable follow-up reminders. While this is just a configurable default, ensure the UI clearly communicates this behavior to avoid surprises.


757-757: LGTM!

The followUpAppliedAt field appropriately tracks when follow-up labels are applied, with nullable type to indicate threads without follow-up labels. This supports idempotent follow-up processing.


766-766: LGTM!

The composite index is well-designed for follow-up reminder queries that filter by account, tracker type, resolution status, and follow-up application state, then order by sent date. The column ordering supports efficient query execution.

Comment on lines +174 to +185
// Remove follow-up label if present (they replied, so follow-up no longer needed)
try {
await clearFollowUpLabel({
emailAccountId,
threadId: actualThreadId,
provider,
logger,
});
} catch (error) {
logger.error("Error removing follow-up label on inbound", { error });
captureException(error, { emailAccountId });
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add guard clause for undefined threadId.

The actualThreadId could theoretically be undefined if both threadId parameter and parsedMessage.threadId are falsy. While rare (email messages typically have thread IDs), this would pass an undefined value to clearFollowUpLabel, which expects a string.

🛡️ Proposed fix
 // Remove follow-up label if present (they replied, so follow-up no longer needed)
+if (actualThreadId) {
   try {
     await clearFollowUpLabel({
       emailAccountId,
       threadId: actualThreadId,
       provider,
       logger,
     });
   } catch (error) {
     logger.error("Error removing follow-up label on inbound", { error });
     captureException(error, { emailAccountId });
   }
+}
📝 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
// Remove follow-up label if present (they replied, so follow-up no longer needed)
try {
await clearFollowUpLabel({
emailAccountId,
threadId: actualThreadId,
provider,
logger,
});
} catch (error) {
logger.error("Error removing follow-up label on inbound", { error });
captureException(error, { emailAccountId });
}
// Remove follow-up label if present (they replied, so follow-up no longer needed)
if (actualThreadId) {
try {
await clearFollowUpLabel({
emailAccountId,
threadId: actualThreadId,
provider,
logger,
});
} catch (error) {
logger.error("Error removing follow-up label on inbound", { error });
captureException(error, { emailAccountId });
}
}
🤖 Prompt for AI Agents
In @apps/web/utils/webhook/process-history-item.ts around lines 174 - 185, Add a
guard to avoid calling clearFollowUpLabel with an undefined thread id: check
that actualThreadId (resolved from the function parameter threadId or
parsedMessage.threadId) is a non-empty string before invoking
clearFollowUpLabel; if it is falsy, skip the call (and the try/catch) so you
don't pass undefined into clearFollowUpLabel, and keep existing
logging/exception capture only around the actual call when actualThreadId is
present.

@jshwrnr jshwrnr closed this Jan 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments