Skip to content

Bulk process#1122

Merged
elie222 merged 9 commits intomainfrom
feat/bulk-process
Dec 20, 2025
Merged

Bulk process#1122
elie222 merged 9 commits intomainfrom
feat/bulk-process

Conversation

@elie222
Copy link
Owner

@elie222 elie222 commented Dec 19, 2025

Summary by CodeRabbit

  • New Features

    • Real-time, batched activity log UI showing sender, subject, status and rule badges; integrated into bulk-run controls with Pause/Resume/Stop.
    • Page-leave protection during active processing.
  • Improvements

    • Reducer-driven bulk-run flow with clearer progress messaging and centralized run controls.
    • Increased AI concurrency to 3 and new queue controls (pause/resume/clear) plus a public clear-queue API.
  • Tests

    • Comprehensive tests covering reducer state transitions and progress messaging.

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

@vercel
Copy link

vercel bot commented Dec 19, 2025

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

Project Deployment Review Updated (UTC)
inbox-zero Ready Ready Preview Dec 20, 2025 5:06pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 19, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds a live batched activity log with SWR polling, a reducer-based bulk-run FSM and tests, AI-queue control helpers and higher concurrency, a beforeunload hook, and landing-page demos; integrates these into bulk-run UI and polls /api/user/executed-rules/batch.

Changes

Cohort / File(s) Summary
Activity Log UI
apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx
New React TSX exporting ActivityLog, BulkProcessActivityLog, and ActivityLogEntry; maintains internal activity log, maps statuses (processing/completed/waiting), polls /api/user/executed-rules/batch via SWR (2s), and renders rows with icons, badges, processing count, loading/paused states.
Bulk run reducer & tests
apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.ts, apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.test.ts
New bulkRunReducer, initialBulkRunState, getProgressMessage; actions include START, THREADS_QUEUED (threads[]), COMPLETE, PAUSE, RESUME, STOP, RESET; tests cover transitions, deduplication, and progress messaging.
Bulk run integration / UI
apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
Refactored to reducer-driven state, adds start/pause/resume/stop flows, beforeunload protection, derived flags (isProcessing/isPaused), integrates BulkProcessActivityLog, and wires ai-queue controls. onRun now passes threads array to onThreadsQueued.
AI queue implementation
apps/web/utils/queue/ai-queue.ts
Increased concurrency (1 → 3); added helpers: pauseAiQueue(), resumeAiQueue(), clearAiQueue(), and isAiQueuePaused().
AI queue store helper
apps/web/store/ai-queue.ts
Added clearAiQueueAtom() to reset the aiQueue atom via the Jotai store.
Before-unload hook
apps/web/hooks/useBeforeUnload.ts
New useBeforeUnload(enabled: boolean) hook that registers/removes a beforeunload listener to prevent navigation when enabled.
Landing page demo
apps/web/app/(landing)/components/page.tsx
Imports ActivityLog and ActivityLogEntry, adds demo scenarios (default, paused, long text, all-completed) and helper getActivityLogEntries() for samples.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant UI as BulkRunRules (UI)
  participant Reducer as bulkRunReducer
  participant Queue as AI Queue (PQueue)
  participant BAL as BulkProcessActivityLog
  participant SWR as SWR hook
  participant API as /api/user/executed-rules/batch

  UI->>Reducer: dispatch START
  UI->>Queue: resumeAiQueue() / enqueue tasks
  UI->>Reducer: dispatch THREADS_QUEUED (threads[])
  UI->>BAL: render with threads, processedThreadIds, aiQueue

  BAL->>SWR: start polling (every 2s) with messageIds
  loop every 2s
    SWR->>API: fetch executed rules batch
    API-->>SWR: executed rules payload
    SWR-->>BAL: executedRulesData
    BAL->>BAL: map executedRules → mark entries completed, set ruleName
    BAL-->>UI: activity log updates (re-render)
  end

  Queue->>Queue: process AI tasks (concurrency 3)
  Queue-->>UI: task complete events
  UI->>Reducer: dispatch COMPLETE / update processedThreadIds
  Reducer-->>UI: updated state (isProcessing/isPaused/stopped)
  UI->>Queue: pauseAiQueue() / resumeAiQueue() / clearAiQueue() (user actions)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Pay attention to reducer state transitions and edge cases (START, THREADS_QUEUED, COMPLETE, STOP).
  • Verify SWR polling logic: messageId extraction, refresh control, and mapping executedRules → entries.
  • Review PQueue concurrency change and pause/resume/clear semantics and integration with atom store.
  • Confirm BulkRunRules ↔ BulkProcessActivityLog prop/state consistency and useBeforeUnload behavior.
  • Check tests for completeness and correctness of reducer behavior.

Possibly related PRs

Suggested reviewers

  • anakarentorosserrano-star

Poem

🐰 Hop, hop, the logs now hum,

Threads in threes are set to run,
Pause a beat, then resume play,
Badges blink and counts convey,
A rabbit cheers the tidy day!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 23.08% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'Bulk process' is vague and generic, providing insufficient clarity about the specific changes introduced in this comprehensive pull request. Use a more descriptive title that captures the main objective, such as 'Add bulk activity log and processing queue management' or 'Implement bulk email processing with live activity tracking'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/bulk-process

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

❤️ Share

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

@macroscopeapp
Copy link
Contributor

macroscopeapp bot commented Dec 19, 2025

Add bulk processing UI with pause/resume/stop controls, a live activity log capped at 50 entries, and raise AI queue concurrency to 3 in BulkRunRules.tsx

Introduce a reducer-driven bulk run flow with queue pause/resume/stop, integrate a polling activity log, add a before-unload guard, and switch the AI queue to concurrency 3. The activity log renders per-email status and rule labels while polling executed rules.

📍Where to Start

Start with the state machine and UI wiring in bulkRunReducer and its usage in bulk-run-rules-reducer.ts, then review the integration in assistant.BulkRunRules in BulkRunRules.tsx.


Macroscope summarized 46d6ca5.

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.

No issues found across 7 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: 0

🧹 Nitpick comments (4)
apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx (2)

76-119: Consider cleanup on component unmount.

The handlers are well-implemented with proper validation and error recovery. However, if the dialog is closed or component unmounts while processing is active, the async operation continues. Consider adding a cleanup effect.

🔎 Suggested cleanup on unmount
+ import { useEffect, useReducer, useRef, useState } from "react";
...
  const abortRef = useRef<() => void>(undefined);

+ // Cleanup on unmount
+ useEffect(() => {
+   return () => {
+     abortRef.current?.();
+   };
+ }, []);

354-358: Consider awaiting the run() call or documenting fire-and-forget intent.

The run() function is called without await, making it fire-and-forget. This is likely intentional (the abort function is returned immediately), but unhandled promise rejections could occur if an unexpected error happens. Consider adding a .catch() handler.

🔎 Suggested error boundary for run()
-  run();
+  run().catch((err) => {
+    console.error("Unexpected error in bulk run:", err);
+    onComplete("error", totalProcessed);
+  });
apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx (2)

147-154: Add error handling for SWR polling.

The SWR hook lacks error handling. While polling failures are non-critical here, handling errors gracefully improves user experience and prevents silent failures.

🔎 Add error handling
  const { data: executedRulesData } = useSWR<BatchExecutedRulesResponse>(
    messageIds.length > 0 && !allCompleted
      ? `/api/user/executed-rules/batch?messageIds=${messageIds.join(",")}`
      : null,
    {
      refreshInterval: messageIds.length > 0 && !allCompleted ? 2000 : 0,
+     onError: (error) => {
+       console.error("Failed to fetch executed rules:", error);
+     },
    },
  );

Alternatively, for user-facing errors:

+ import { toastError } from "@/components/Toast";

- const { data: executedRulesData } = useSWR<BatchExecutedRulesResponse>(
+ const { data: executedRulesData, error: executedRulesError } = useSWR<BatchExecutedRulesResponse>(
    messageIds.length > 0 && !allCompleted
      ? `/api/user/executed-rules/batch?messageIds=${messageIds.join(",")}`
      : null,
    {
      refreshInterval: messageIds.length > 0 && !allCompleted ? 2000 : 0,
    },
  );

+ useEffect(() => {
+   if (executedRulesError?.serverError) {
+     toastError({ description: "Failed to fetch rule execution status" });
+   }
+ }, [executedRulesError]);

As per coding guidelines, async operations should use result?.serverError with toastError for error handling.


33-56: Consider mobile-responsive max-height.

The fixed max-h-72 (18rem) on line 43 works on desktop but might be too tall on smaller mobile screens, potentially pushing other content off-screen.

🔎 Responsive height adjustment
-     <div className="max-h-72 overflow-y-auto overflow-x-hidden">
+     <div className="max-h-48 overflow-y-auto overflow-x-hidden sm:max-h-72">

As per coding guidelines, implement responsive design with Tailwind CSS using a mobile-first approach.

📜 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 5baec32 and 4ecf18a.

📒 Files selected for processing (7)
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx (1 hunks)
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx (7 hunks)
  • apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.test.ts (1 hunks)
  • apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.ts (1 hunks)
  • apps/web/app/(landing)/components/page.tsx (3 hunks)
  • apps/web/store/ai-queue.ts (1 hunks)
  • apps/web/utils/queue/ai-queue.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (21)
**/*.{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/store/ai-queue.ts
  • apps/web/utils/queue/ai-queue.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.ts
  • apps/web/app/(landing)/components/page.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.test.ts
**/*.{ts,tsx,js,jsx}

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

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

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

Files:

  • apps/web/store/ai-queue.ts
  • apps/web/utils/queue/ai-queue.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.ts
  • apps/web/app/(landing)/components/page.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.test.ts
apps/web/**/*.{ts,tsx}

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

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

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

Files:

  • apps/web/store/ai-queue.ts
  • apps/web/utils/queue/ai-queue.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.ts
  • apps/web/app/(landing)/components/page.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.test.ts
**/*.ts

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

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

Files:

  • apps/web/store/ai-queue.ts
  • apps/web/utils/queue/ai-queue.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.test.ts
**/*.{tsx,ts}

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

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

Files:

  • apps/web/store/ai-queue.ts
  • apps/web/utils/queue/ai-queue.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.ts
  • apps/web/app/(landing)/components/page.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.test.ts
**/*.{tsx,ts,css}

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

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

Files:

  • apps/web/store/ai-queue.ts
  • apps/web/utils/queue/ai-queue.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.ts
  • apps/web/app/(landing)/components/page.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.test.ts
**/*.{js,jsx,ts,tsx}

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

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

Files:

  • apps/web/store/ai-queue.ts
  • apps/web/utils/queue/ai-queue.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.ts
  • apps/web/app/(landing)/components/page.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.test.ts
!(pages/_document).{jsx,tsx}

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

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

Files:

  • apps/web/store/ai-queue.ts
  • apps/web/utils/queue/ai-queue.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.ts
  • apps/web/app/(landing)/components/page.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.test.ts
**/*.{js,ts,jsx,tsx}

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

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

Files:

  • apps/web/store/ai-queue.ts
  • apps/web/utils/queue/ai-queue.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.ts
  • apps/web/app/(landing)/components/page.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.test.ts
apps/web/**/*.{ts,tsx,js,jsx}

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

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

Files:

  • apps/web/store/ai-queue.ts
  • apps/web/utils/queue/ai-queue.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.ts
  • apps/web/app/(landing)/components/page.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.test.ts
apps/web/**/*.{ts,tsx,js,jsx,json,css,md}

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

Format code with Prettier

Files:

  • apps/web/store/ai-queue.ts
  • apps/web/utils/queue/ai-queue.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.ts
  • apps/web/app/(landing)/components/page.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.test.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/queue/ai-queue.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/bulk-run-rules-reducer.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.test.ts
apps/web/**/app/**

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

Follow NextJS app router structure with (app) directory

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.ts
  • apps/web/app/(landing)/components/page.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.test.ts
**/{pages,routes,components}/**/*.{ts,tsx}

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

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

Files:

  • apps/web/app/(landing)/components/page.tsx
**/*.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/(landing)/components/page.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.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/(landing)/components/page.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
apps/web/**/*.{tsx,jsx}

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

apps/web/**/*.{tsx,jsx}: Prefer functional components with hooks over class components
Use shadcn/ui components when available
Follow consistent naming conventions with PascalCase for component names
Use LoadingContent component for async data with loading and error states
Use result?.serverError with toastError and toastSuccess for mutation error handling

Files:

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

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

Ensure responsive design with mobile-first approach

Files:

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

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

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.test.ts
**/*.{test,spec}.{js,jsx,ts,tsx}

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

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

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.test.ts
🧠 Learnings (18)
📚 Learning: 2025-11-25T14:38:07.606Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-11-25T14:38:07.606Z
Learning: Applies to apps/web/utils/ai/**/*.ts : Keep related AI functions in the same file or directory, extract common patterns into utility functions, and document complex AI logic with clear comments

Applied to files:

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

Applied to files:

  • apps/web/utils/queue/ai-queue.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/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
📚 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/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.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 : 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/(landing)/components/page.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx
📚 Learning: 2025-12-17T11:18:06.818Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-17T11:18:06.818Z
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/BulkProcessActivityLog.tsx
📚 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/(app)/[emailAccountId]/assistant/BulkRunRules.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/BulkRunRules.tsx
📚 Learning: 2025-06-05T09:49:12.168Z
Learnt from: elie222
Repo: elie222/inbox-zero PR: 485
File: apps/web/app/(landing)/login/page.tsx:41-43
Timestamp: 2025-06-05T09:49:12.168Z
Learning: In Next.js App Router, components that use the `useSearchParams` hook require a Suspense boundary to handle the asynchronous nature of search parameter access. The Suspense wrapper is necessary and should not be removed when a component uses useSearchParams.

Applied to files:

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

Applied to files:

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

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
📚 Learning: 2025-11-25T14:40:13.649Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-11-25T14:40:13.649Z
Learning: Applies to **/*.{tsx,ts,jsx,js} : For API get requests to server, use the `swr` package with `useSWR` hook

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
📚 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/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.test.ts
📚 Learning: 2025-11-25T14:40:00.833Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-25T14:40:00.833Z
Learning: Applies to **/*.test.{ts,tsx} : Use test helpers `getEmail`, `getEmailAccount`, and `getRule` from `@/__tests__/helpers` for mocking emails, accounts, and rules

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.test.ts
📚 Learning: 2025-11-25T14:40:00.833Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-25T14:40:00.833Z
Learning: Applies to **/*.test.{ts,tsx} : Use descriptive test names

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.test.ts
📚 Learning: 2025-11-25T14:40:00.833Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-11-25T14:40:00.833Z
Learning: Applies to **/*.test.{ts,tsx} : Avoid testing implementation details

Applied to files:

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

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.test.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 **/*.test.ts : Include security tests in test suites to verify: authentication is required, IDOR protection works (other users cannot access resources), parameter validation rejects invalid inputs, and error messages don't leak information

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.test.ts
🧬 Code graph analysis (4)
apps/web/app/(landing)/components/page.tsx (1)
apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx (2)
  • ActivityLog (18-58)
  • ActivityLogEntry (10-16)
apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx (4)
apps/web/components/new-landing/common/Badge.tsx (1)
  • Badge (24-99)
apps/web/app/api/threads/route.ts (1)
  • ThreadsResponse (56-56)
apps/web/app/api/user/executed-rules/batch/route.ts (1)
  • BatchExecutedRulesResponse (8-8)
apps/web/utils/queue/ai-queue.ts (1)
  • aiQueue (6-6)
apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx (3)
apps/web/utils/queue/ai-queue.ts (3)
  • resumeAiQueue (9-9)
  • pauseAiQueue (8-8)
  • clearAiQueue (10-10)
apps/web/app/(app)/[emailAccountId]/assistant/SetDateDropdown.tsx (1)
  • SetDateDropdown (14-57)
apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx (1)
  • BulkProcessActivityLog (115-243)
apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.test.ts (1)
apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.ts (4)
  • bulkRunReducer (26-96)
  • initialBulkRunState (19-24)
  • BulkRunState (3-8)
  • getProgressMessage (98-115)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: test
  • GitHub Check: Review for correctness
🔇 Additional comments (13)
apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.test.ts (1)

1-328: LGTM! Comprehensive test coverage for the reducer and progress message utility.

The test suite effectively covers all reducer actions (START, THREADS_QUEUED, COMPLETE, PAUSE, RESUME, STOP, RESET) with proper state transition verification and edge case handling. Tests are well-organized, independent, and follow the project's testing conventions with descriptive names.

apps/web/store/ai-queue.ts (1)

29-31: LGTM! Consistent implementation with existing atom update patterns.

The clearAiQueueAtom function follows the same pattern as pushToAiQueueAtom and removeFromAiQueueAtom, correctly resetting the atom to an empty Set.

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

42-45: LGTM! Proper import of ActivityLog component for demo purposes.

The import follows the project's path alias conventions and correctly imports both the component and its type.


560-628: Good demo coverage for the ActivityLog component.

The demo section effectively showcases various states of the ActivityLog component including default mixed states, paused state, long text truncation handling, and all-completed state. This provides good visual testing coverage.


792-828: LGTM! Helper function correctly placed at bottom of file.

The getActivityLogEntries helper follows the coding guidelines by being placed at the bottom of the file and provides realistic sample data for the demo.

apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx (2)

3-41: LGTM! Clean imports following project conventions.

The imports are well-organized, using the @/ path alias consistently. The separation of queue controls from utils/queue/ai-queue.ts and atom operations from @/store/ai-queue is appropriate.


131-260: Well-structured UI with proper state-based rendering.

The dialog content correctly uses LoadingContent for async states, conditionally renders controls based on processing state, and properly disables inputs when processing is active. The integration with BulkProcessActivityLog provides good user feedback.

apps/web/utils/queue/ai-queue.ts (1)

5-11: Concurrency level increase appears safe for Gmail API constraints.

Increasing concurrency to 3 improves bulk processing speed. The Gmail API has a per-user-per-second limit of 250 quota units, and a concurrency level of 3 is conservative enough to respect this limit and per-user concurrent request constraints. The queue control helper functions are well-implemented wrappers.

The exports are properly separated: apps/web/store/ai-queue.ts handles state management while apps/web/utils/queue/ai-queue.ts provides the queue instance and control functions—no duplication exists.

apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.ts (2)

1-96: LGTM! Well-structured reducer implementation.

The reducer follows React best practices with immutable state updates, clear action types, and proper state transition guards. The logic for handling pause/resume/stop states is sound, and the protection against overriding stopped state (lines 53, 83) prevents race conditions.


98-115: LGTM! Clear progress messaging logic.

The function correctly handles different state scenarios and provides appropriate user feedback for idle, processing, stopped, and completed states.

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

157-193: LGTM! Robust activity log state management.

The effect correctly:

  • Prevents duplicate entries using existingMessageIds Set
  • Handles initial status based on executed rules data
  • Maintains a fixed-size log (last 50 entries)
  • Uses immutable updates

The dependency on processedThreadIds (a Set) works correctly since the reducer creates new Set instances on updates.


196-214: LGTM! Clean completion status updates.

The effect efficiently updates entries to "completed" status when rule execution data becomes available, preserving already-completed entries.


8-8: The Badge import and usage are correct. The component at @/components/Badge uses a color prop, and lines 86-92 correctly pass the color prop. The separate Badge component at @/components/new-landing/common/Badge.tsx uses variant, but that is a different component not being imported here. No issues found.

Likely an incorrect or invalid review comment.

// Get message IDs from processed threads
const messageIds = Array.from(processedThreadIds)
.map((threadId) => {
const thread = threads.find((t) => t.id === threadId);
Copy link
Contributor

Choose a reason for hiding this comment

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

threads.find(...) assumes processed threads are in the current inbox list. When bulk-processing older threads, this returns undefined and those entries are skipped, so the log looks empty/incomplete. Consider decoupling from the inbox snapshot: pass a complete thread/message map (or fetch missing threads by ID) from the bulk-run flow so all processed IDs are represented; otherwise, consider documenting that the log only shows currently visible threads.

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

👍 Helpful? React to give us feedback.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (3)
apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx (2)

67-74: Consider refining isProcessing to prevent UI state mismatch.

The current logic isActive || queue.size > 0 keeps controls visible when status is "processing" but the queue is empty. This can occur after COMPLETE is dispatched (which keeps status as "processing") but before the user manually stops, leaving pause/stop buttons visible with nothing to control.

However, using only queue.size > 0 would hide controls during the fetching phase before items are queued. A more robust solution might be to track fetching separately in the reducer state (e.g., isFetching: boolean) or ensure the queue is always resumed on start and the reducer transitions to "idle" when both fetching completes and the queue is empty.

Related to past review feedback about isProcessing staying true incorrectly.


106-115: Resume the AI queue at the start of each run.

If a previous run was paused and then stopped, the queue remains in a paused state. Starting a new run without resuming would enqueue tasks that never execute until the user manually resumes. Call resumeAiQueue() at the beginning of handleStart to ensure the queue is always active when starting a new run.

🔎 Proposed fix
  const handleStart = async () => {
    dispatch({ type: "START" });
+   resumeAiQueue();

    if (!startDate) {

Related to past review feedback about ensuring the queue is resumed on start.

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

134-141: Activity log may not show threads outside the current inbox view.

The threads.find() call assumes all processed threads exist in the threads array (from the inbox query). When bulk-processing threads older than the current page or outside the inbox (e.g., archived), find returns undefined and those threads are filtered out, resulting in an incomplete activity log.

Consider one of these approaches:

  1. Pass a dedicated thread map that accumulates all processed threads rather than relying on the inbox snapshot
  2. Fetch missing threads by ID when needed
  3. Document this limitation in the component or UI if it's an acceptable trade-off

Related to past review feedback about the threads.find() limitation.

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

128-133: Consider resuming the queue in handleStop for defensive cleanup.

While the queue is cleared, it may still be left in a paused state if the user paused before stopping. Although resuming on start (as suggested above) addresses this, defensively calling resumeAiQueue() here ensures the queue is always reset to a ready state after stopping.

🔎 Proposed addition
  const handleStop = () => {
    dispatch({ type: "STOP", completedCount: completed });
    clearAiQueue();
    clearAiQueueAtom();
+   resumeAiQueue();
    abortRef.current?.();
  };
apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx (1)

158-195: Consider removing executedRulesData from effect dependencies for efficiency.

Including executedRulesData in the dependency array causes this effect to re-run every 2 seconds (the SWR polling interval), even when no new threads are added. While the duplicate check prevents incorrect behavior, the effect still executes the entire loop unnecessarily.

Since the second effect (lines 197-216) already handles updating entries when executedRulesData changes, consider removing executedRulesData from this effect's dependencies and always initializing new entries with status: "processing". The second effect will update them to "completed" when the data arrives.

🔎 Proposed refactor
-  }, [processedThreadIds, executedRulesData, threads]);
+  }, [processedThreadIds, threads]);

And update the entry initialization:

      newEntries.push({
        threadId: thread.id,
        messageId: message.id,
        from: message.headers.from || "Unknown",
        subject: message.headers.subject || "(No subject)",
-       status: executedRule ? "completed" : "processing",
+       status: "processing",
-       ruleName: executedRule?.rule?.name,
        timestamp: Date.now(),
      });
📜 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 4ecf18a and e9d1e2d.

📒 Files selected for processing (2)
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx (1 hunks)
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx (6 hunks)
🧰 Additional context used
📓 Path-based instructions (16)
**/*.{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/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.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/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.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/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.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
Use @/ path aliases for imports from project root
Follow tailwindcss patterns with prettier-plugin-tailwindcss class organization
Prefix client-side environment variables with NEXT_PUBLIC_
Leverage TypeScript inference for better developer experience with type exports from API routes

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.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/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.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/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.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/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.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/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.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/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.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/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.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/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx
apps/web/**/app/**

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

Follow NextJS app router structure with (app) directory

Files:

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

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

apps/web/**/*.{tsx,jsx}: Prefer functional components with hooks over class components
Use shadcn/ui components when available
Follow consistent naming conventions with PascalCase for component names
Use LoadingContent component for async data with loading and error states
Use result?.serverError with toastError and toastSuccess for mutation error handling

Files:

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

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

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

Files:

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

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

Format code with Prettier

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.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/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx
🧠 Learnings (6)
📚 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/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.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/BulkRunRules.tsx
📚 Learning: 2025-06-05T09:49:12.168Z
Learnt from: elie222
Repo: elie222/inbox-zero PR: 485
File: apps/web/app/(landing)/login/page.tsx:41-43
Timestamp: 2025-06-05T09:49:12.168Z
Learning: In Next.js App Router, components that use the `useSearchParams` hook require a Suspense boundary to handle the asynchronous nature of search parameter access. The Suspense wrapper is necessary and should not be removed when a component uses useSearchParams.

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.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 : 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/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx
📚 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/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx
📚 Learning: 2025-12-17T11:18:06.818Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-17T11:18:06.818Z
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/BulkProcessActivityLog.tsx
🧬 Code graph analysis (1)
apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx (3)
apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.ts (3)
  • bulkRunReducer (26-96)
  • initialBulkRunState (19-24)
  • getProgressMessage (98-115)
apps/web/utils/queue/ai-queue.ts (3)
  • resumeAiQueue (9-9)
  • pauseAiQueue (8-8)
  • clearAiQueue (10-10)
apps/web/store/ai-queue.ts (1)
  • clearAiQueueAtom (29-31)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: test
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: Review for correctness
🔇 Additional comments (5)
apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx (2)

77-88: Good use of beforeunload protection.

The implementation correctly prevents users from accidentally leaving the page during active processing, providing a safety net for long-running operations.


118-126: LGTM!

The pause/resume handler correctly synchronizes both the queue state and the reducer state, ensuring UI and processing stay in sync.

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

18-58: Well-structured presentational component.

The ActivityLog component follows good React patterns with clear props, appropriate null-rendering when empty, and clean visual hierarchy. The scrollable container with max height is a good UX choice for potentially long lists.


197-216: Efficient completion tracking.

This effect correctly updates entries when execution completes, with appropriate guards to skip already-completed entries and avoid unnecessary state updates.


218-244: LGTM!

The transformation from internal to public format is clean and the status derivation logic correctly reflects the current processing state based on queue membership and completion status.

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.

2 issues found across 2 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/hooks/useBeforeUnload.ts">

<violation number="1" location="apps/web/hooks/useBeforeUnload.ts:12">
P2: Missing `e.returnValue` assignment for cross-browser compatibility. Some browsers (particularly Safari) require `e.returnValue` to be set to trigger the native &quot;leave page?&quot; dialog. Without it, the hook may silently fail.</violation>
</file>

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

<violation number="1" location="apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx:74">
P2: Race condition: Button remains clickable after click until queue is populated. The old logic `isActive || queue.size &gt; 0` hid the button immediately on START dispatch, but the new `queue.size &gt; 0` leaves a window where the button is still visible/clickable. Consider keeping `isActive` in the condition or adding `isActive` to the button&#39;s disabled prop.</violation>
</file>

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx (1)

96-109: Race condition: abortRef.current may not be set when Stop is clicked.

Line 97 correctly resumes the queue before starting (addressing a previous review concern). However, the race condition flagged in past reviews remains: handleStop can be invoked immediately after the user clicks "Process Emails," but before the async onRun(...) on Line 99 returns and assigns the abort function to abortRef.current. In this scenario, Line 125's abortRef.current?.() would be a no-op, leaving the background fetching unaborted.

Suggested fix: Set abortRef immediately with a buffered abort
    // Ensure queue is not paused from a previous run
    resumeAiQueue();

-   abortRef.current = await onRun(
+   let pendingAbort = false;
+   abortRef.current = () => {
+     pendingAbort = true;
+   };
+
+   const abort = await onRun(
      emailAccountId,
      { startDate, endDate, includeRead },
      (ids) => {
        dispatch({ type: "THREADS_QUEUED", ids });
      },
      (_completionStatus, count) => {
        dispatch({ type: "COMPLETE", count });
      },
    );
+
+   if (pendingAbort) {
+     abort();
+   } else {
+     abortRef.current = abort;
+   }

This ensures that if handleStop is called early, the abort is buffered and executed once onRun resolves.

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

121-126: Consider resuming the queue in handleStop for defensive programming.

While Line 97 in handleStart ensures the queue is resumed before each run, previous review feedback suggests also calling resumeAiQueue() in handleStop to guarantee the queue isn't left in a paused state between runs. This makes the stop operation more self-contained and prevents subtle state bugs if the start flow changes.

Proposed change
  const handleStop = () => {
    dispatch({ type: "STOP", completedCount: completed });
    clearAiQueue();
    clearAiQueueAtom();
+   resumeAiQueue();
    abortRef.current?.();
  };

Based on learnings from previous reviews.

📜 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 e9d1e2d and df037ed.

📒 Files selected for processing (2)
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx (6 hunks)
  • apps/web/hooks/useBeforeUnload.ts (1 hunks)
🧰 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/hooks/useBeforeUnload.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
apps/web/hooks/use*.ts

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

apps/web/hooks/use*.ts: Use SWR hooks for client-side data fetching, with hooks stored in apps/web/hooks/use*.ts that return typed responses from GET API routes
Call mutate() on SWR hooks after successful mutations to refresh cached data

apps/web/hooks/use*.ts: Use the use prefix for custom hook filenames (e.g., useAccounts.ts)
For data fetching in custom hooks, prefer using useSWR and wrap it to handle API endpoint URL, returning data, loading state, error state, and potentially the mutate function
Create dedicated hooks for specific data types (e.g., useAccounts, useLabels) to wrap useSWR for individual API endpoints

Files:

  • apps/web/hooks/useBeforeUnload.ts
apps/web/hooks/**/*.ts

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

Place custom hooks in the apps/web/hooks/ directory

Files:

  • apps/web/hooks/useBeforeUnload.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/hooks/useBeforeUnload.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.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
Use @/ path aliases for imports from project root
Follow tailwindcss patterns with prettier-plugin-tailwindcss class organization
Prefix client-side environment variables with NEXT_PUBLIC_
Leverage TypeScript inference for better developer experience with type exports from API routes

Files:

  • apps/web/hooks/useBeforeUnload.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
**/*.ts

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

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

Files:

  • apps/web/hooks/useBeforeUnload.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/hooks/useBeforeUnload.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.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/hooks/useBeforeUnload.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.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/hooks/useBeforeUnload.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.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/hooks/useBeforeUnload.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.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/hooks/useBeforeUnload.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
apps/web/**/*.{ts,tsx,js,jsx}

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

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

Files:

  • apps/web/hooks/useBeforeUnload.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
apps/web/**/*.{ts,tsx,js,jsx,json,css,md}

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

Format code with Prettier

Files:

  • apps/web/hooks/useBeforeUnload.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
apps/web/**/hooks/*.ts

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

Use SWR for client-side data fetching with type-safe response types

Files:

  • apps/web/hooks/useBeforeUnload.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/BulkRunRules.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/BulkRunRules.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/BulkRunRules.tsx
apps/web/**/app/**

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

Follow NextJS app router structure with (app) directory

Files:

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

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

apps/web/**/*.{tsx,jsx}: Prefer functional components with hooks over class components
Use shadcn/ui components when available
Follow consistent naming conventions with PascalCase for component names
Use LoadingContent component for async data with loading and error states
Use result?.serverError with toastError and toastSuccess for mutation error handling

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.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/BulkRunRules.tsx
🧠 Learnings (21)
📚 Learning: 2025-11-25T14:37:35.343Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/hooks.mdc:0-0
Timestamp: 2025-11-25T14:37:35.343Z
Learning: Applies to apps/web/hooks/use*.ts : Use the `use` prefix for custom hook filenames (e.g., `useAccounts.ts`)

Applied to files:

  • apps/web/hooks/useBeforeUnload.ts
📚 Learning: 2025-11-25T14:38:27.988Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/posthog-feature-flags.mdc:0-0
Timestamp: 2025-11-25T14:38:27.988Z
Learning: Applies to apps/web/hooks/useFeatureFlags.ts : All feature flag hooks should be defined in `apps/web/hooks/useFeatureFlags.ts`

Applied to files:

  • apps/web/hooks/useBeforeUnload.ts
📚 Learning: 2025-11-25T14:38:32.328Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/posthog-feature-flags.mdc:0-0
Timestamp: 2025-11-25T14:38:32.328Z
Learning: Applies to apps/web/hooks/useFeatureFlags.ts : Feature flag hooks should be defined in `apps/web/hooks/useFeatureFlags.ts` with two patterns: boolean flags using `useFeatureFlagEnabled("key")` and variant flags using `useFeatureFlagVariantKey("key")` with type casting

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • apps/web/hooks/useBeforeUnload.ts
📚 Learning: 2025-11-25T14:38:32.328Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/posthog-feature-flags.mdc:0-0
Timestamp: 2025-11-25T14:38:32.328Z
Learning: Centralize all feature flag hooks in `apps/web/hooks/useFeatureFlags.ts` rather than scattering them across multiple files

Applied to files:

  • apps/web/hooks/useBeforeUnload.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/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
📚 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/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
📚 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/(app)/[emailAccountId]/assistant/BulkRunRules.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/BulkRunRules.tsx
📚 Learning: 2025-06-05T09:49:12.168Z
Learnt from: elie222
Repo: elie222/inbox-zero PR: 485
File: apps/web/app/(landing)/login/page.tsx:41-43
Timestamp: 2025-06-05T09:49:12.168Z
Learning: In Next.js App Router, components that use the `useSearchParams` hook require a Suspense boundary to handle the asynchronous nature of search parameter access. The Suspense wrapper is necessary and should not be removed when a component uses useSearchParams.

Applied to files:

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

Applied to files:

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

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
📚 Learning: 2025-11-25T14:40:13.649Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-11-25T14:40:13.649Z
Learning: Applies to **/*.{tsx,ts,jsx,js} : For API get requests to server, use the `swr` package with `useSWR` hook

Applied to files:

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

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
📚 Learning: 2025-12-17T11:18:06.818Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-17T11:18:06.818Z
Learning: Applies to apps/web/**/hooks/*.ts : Use SWR for client-side data fetching with type-safe response types

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.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 : Use SWR hooks for client-side data fetching, with hooks stored in `apps/web/hooks/use*.ts` that return typed responses from GET API routes

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
🧬 Code graph analysis (1)
apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx (5)
apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.ts (3)
  • bulkRunReducer (26-96)
  • initialBulkRunState (19-24)
  • getProgressMessage (98-115)
apps/web/hooks/useBeforeUnload.ts (1)
  • useBeforeUnload (7-20)
apps/web/utils/queue/ai-queue.ts (3)
  • resumeAiQueue (9-9)
  • pauseAiQueue (8-8)
  • clearAiQueue (10-10)
apps/web/store/ai-queue.ts (1)
  • clearAiQueueAtom (29-31)
apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx (1)
  • BulkProcessActivityLog (117-245)
⏰ 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). (4)
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: test
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Review for correctness
🔇 Additional comments (4)
apps/web/hooks/useBeforeUnload.ts (1)

1-20: LGTM! Well-implemented beforeunload protection hook.

The hook correctly follows React patterns and beforeunload best practices. The implementation properly prevents page navigation when enabled, uses appropriate cleanup, and has correct dependencies.

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

68-78: Well-structured derived state and navigation protection.

The derived state calculations cleanly separate concerns, and Line 74 correctly derives isProcessing from queue.size > 0 rather than relying on status, which addresses the concern from previous reviews. The useBeforeUnload hook appropriately prevents accidental navigation during active operations.


111-119: LGTM! Clean pause/resume logic.

The handler correctly synchronizes the AI queue state with the reducer state, ensuring both pause and resume operations update the queue and dispatch the appropriate action.


141-247: Excellent state-driven UI updates.

The UI correctly reflects the processing state throughout:

  • Date inputs and toggles are appropriately disabled during active processing (Lines 167, 176, 186)
  • The activity log is conditionally shown when there's processing state to display (Lines 202-212)
  • Button visibility and content dynamically adapt to whether processing is active (Lines 214-247)
  • Pause/Resume toggle provides clear visual feedback with icons (Lines 226-236)

The refactor successfully coordinates reducer state with UI presentation.

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

♻️ Duplicate comments (4)
apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx (4)

73-73: Race condition: Button remains clickable between START and queue population.

The isProcessing flag only checks queue.size > 0, which means there's a window between dispatching START (line 80) and threads being added to the queue (line 102) where the "Process Emails" button remains visible and clickable. This allows users to trigger multiple concurrent runs.

🔎 Proposed fix

Include state.status in the check to hide the button immediately after START is dispatched:

- const isProcessing = queue.size > 0;
+ const isProcessing = state.status === "processing" || queue.size > 0;

98-107: Race condition: handleStop may be ineffective if called early.

The abortRef.current is assigned after the await onRun(...) call completes. If a user clicks Stop before onRun returns the abort function, the abortRef.current?.() call in handleStop (line 124) won't abort the ongoing fetch loop. This leaves the background fetch process running even though the UI shows "stopped".

Consider one of these approaches:

  1. Return the abort function synchronously from onRun (change run() on line 364 to start the async loop in the background)
  2. Buffer the stop request and invoke abort immediately once abortRef.current is set
  3. Add a separate abort signal that onRun checks before starting each iteration

120-125: Missing queue resume on stop could block subsequent runs.

If the user stops processing while the queue is paused, pauseAiQueue() state persists. The next time they click "Process Emails", new tasks will be enqueued into a paused queue and won't execute until manually resumed. Always call resumeAiQueue() in handleStop to reset the queue to a clean state.

🔎 Proposed fix
  const handleStop = () => {
    dispatch({ type: "STOP", completedCount: completed });
+   resumeAiQueue();
    clearAiQueue();
    clearAiQueueAtom();
    abortRef.current?.();
  };

215-223: Button visibility subject to same race condition as line 73.

The button is only hidden when !isProcessing, which doesn't account for state.status. This is the same race condition flagged at line 73 — the button remains clickable between START dispatch and queue population.

See the proposed fix at line 73.

📜 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 df037ed and 8403bc1.

📒 Files selected for processing (1)
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx (6 hunks)
🧰 Additional context used
📓 Path-based instructions (16)
**/*.{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/BulkRunRules.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/BulkRunRules.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/BulkRunRules.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
Use @/ path aliases for imports from project root
Follow tailwindcss patterns with prettier-plugin-tailwindcss class organization
Prefix client-side environment variables with NEXT_PUBLIC_
Leverage TypeScript inference for better developer experience with type exports from API routes

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.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/BulkRunRules.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/BulkRunRules.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/BulkRunRules.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/BulkRunRules.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/BulkRunRules.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/BulkRunRules.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/BulkRunRules.tsx
apps/web/**/app/**

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

Follow NextJS app router structure with (app) directory

Files:

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

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

apps/web/**/*.{tsx,jsx}: Prefer functional components with hooks over class components
Use shadcn/ui components when available
Follow consistent naming conventions with PascalCase for component names
Use LoadingContent component for async data with loading and error states
Use result?.serverError with toastError and toastSuccess for mutation error handling

Files:

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

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

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

Files:

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

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

Format code with Prettier

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.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/BulkRunRules.tsx
🧠 Learnings (10)
📚 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/(app)/[emailAccountId]/assistant/BulkRunRules.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/BulkRunRules.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/BulkRunRules.tsx
📚 Learning: 2025-06-05T09:49:12.168Z
Learnt from: elie222
Repo: elie222/inbox-zero PR: 485
File: apps/web/app/(landing)/login/page.tsx:41-43
Timestamp: 2025-06-05T09:49:12.168Z
Learning: In Next.js App Router, components that use the `useSearchParams` hook require a Suspense boundary to handle the asynchronous nature of search parameter access. The Suspense wrapper is necessary and should not be removed when a component uses useSearchParams.

Applied to files:

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

Applied to files:

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

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
📚 Learning: 2025-11-25T14:40:13.649Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-11-25T14:40:13.649Z
Learning: Applies to **/*.{tsx,ts,jsx,js} : For API get requests to server, use the `swr` package with `useSWR` hook

Applied to files:

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

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
📚 Learning: 2025-12-17T11:18:06.818Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-17T11:18:06.818Z
Learning: Applies to apps/web/**/hooks/*.ts : Use SWR for client-side data fetching with type-safe response types

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.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 : Use SWR hooks for client-side data fetching, with hooks stored in `apps/web/hooks/use*.ts` that return typed responses from GET API routes

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.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). (3)
  • GitHub Check: test
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Review for correctness

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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

236-257: Missing type attribute on buttons.

Per coding guidelines, all button elements should include a type attribute. The Pause/Resume and Stop buttons are missing this attribute.

🔎 Proposed fix
-                        <Button size="sm" onClick={handlePauseResume}>
+                        <Button type="button" size="sm" onClick={handlePauseResume}>
                           {isPaused ? (
                             <>
                               <PlayIcon className="mr-1.5 h-3.5 w-3.5" />
                               Resume
                             </>
                           ) : (
                             <>
                               <PauseIcon className="mr-1.5 h-3.5 w-3.5" />
                               Pause
                             </>
                           )}
                         </Button>
                         <Button
                           variant="outline"
                           size="sm"
+                          type="button"
                           onClick={handleStop}
                         >
apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx (2)

134-139: Refine the clearing condition to prevent mid-run clears.

The current implementation clears the log whenever loading becomes true, but if loading toggles during a run, this might unexpectedly clear the log. Per the previous review comment, consider clearing only when starting a fresh run.

🔎 More precise clearing condition
  useEffect(() => {
-   if (loading) {
+   if (loading && processedThreadIds.size === 0) {
      setActivityLog([]);
    }
- }, [loading]);
+ }, [loading, processedThreadIds]);

239-242: Consider memoizing the processing count.

The processingCount is recalculated on every render. While the performance impact is likely minimal, memoizing it would follow React best practices.

🔎 Memoized version
+ import { useEffect, useState, useMemo } from "react";

  // ... rest of component

- const processingCount = activityLog.filter(
-   (entry) => aiQueue.has(entry.threadId) && entry.status !== "completed",
- ).length;
+ const processingCount = useMemo(
+   () =>
+     activityLog.filter(
+       (entry) => aiQueue.has(entry.threadId) && entry.status !== "completed",
+     ).length,
+   [activityLog, aiQueue],
+ );
📜 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 8403bc1 and 82a41b0.

📒 Files selected for processing (3)
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx (1 hunks)
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx (6 hunks)
  • apps/web/hooks/useBeforeUnload.ts (1 hunks)
🧰 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/hooks/useBeforeUnload.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx
apps/web/hooks/use*.ts

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

apps/web/hooks/use*.ts: Use SWR hooks for client-side data fetching, with hooks stored in apps/web/hooks/use*.ts that return typed responses from GET API routes
Call mutate() on SWR hooks after successful mutations to refresh cached data

apps/web/hooks/use*.ts: Use the use prefix for custom hook filenames (e.g., useAccounts.ts)
For data fetching in custom hooks, prefer using useSWR and wrap it to handle API endpoint URL, returning data, loading state, error state, and potentially the mutate function
Create dedicated hooks for specific data types (e.g., useAccounts, useLabels) to wrap useSWR for individual API endpoints

Files:

  • apps/web/hooks/useBeforeUnload.ts
apps/web/hooks/**/*.ts

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

Place custom hooks in the apps/web/hooks/ directory

Files:

  • apps/web/hooks/useBeforeUnload.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/hooks/useBeforeUnload.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.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
Use @/ path aliases for imports from project root
Follow tailwindcss patterns with prettier-plugin-tailwindcss class organization
Prefix client-side environment variables with NEXT_PUBLIC_
Leverage TypeScript inference for better developer experience with type exports from API routes

Files:

  • apps/web/hooks/useBeforeUnload.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx
**/*.ts

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

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

Files:

  • apps/web/hooks/useBeforeUnload.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/hooks/useBeforeUnload.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.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/hooks/useBeforeUnload.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.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/hooks/useBeforeUnload.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.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/hooks/useBeforeUnload.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.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/hooks/useBeforeUnload.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx
apps/web/**/*.{ts,tsx,js,jsx}

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

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

Files:

  • apps/web/hooks/useBeforeUnload.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx
apps/web/**/*.{ts,tsx,js,jsx,json,css,md}

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

Format code with Prettier

Files:

  • apps/web/hooks/useBeforeUnload.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx
apps/web/**/hooks/*.ts

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

Use SWR for client-side data fetching with type-safe response types

Files:

  • apps/web/hooks/useBeforeUnload.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/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.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/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.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/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx
apps/web/**/app/**

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

Follow NextJS app router structure with (app) directory

Files:

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

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

apps/web/**/*.{tsx,jsx}: Prefer functional components with hooks over class components
Use shadcn/ui components when available
Follow consistent naming conventions with PascalCase for component names
Use LoadingContent component for async data with loading and error states
Use result?.serverError with toastError and toastSuccess for mutation error handling

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.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/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx
🧠 Learnings (22)
📚 Learning: 2025-11-25T14:38:27.988Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/posthog-feature-flags.mdc:0-0
Timestamp: 2025-11-25T14:38:27.988Z
Learning: Applies to apps/web/hooks/useFeatureFlags.ts : For early access features, create boolean flag hooks using `useFeatureFlagEnabled` with the pattern `export function use[FeatureName]Enabled()`

Applied to files:

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

Applied to files:

  • apps/web/hooks/useBeforeUnload.ts
📚 Learning: 2025-11-25T14:38:32.328Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/posthog-feature-flags.mdc:0-0
Timestamp: 2025-11-25T14:38:32.328Z
Learning: Applies to apps/web/hooks/useFeatureFlags.ts : Feature flag hooks should be defined in `apps/web/hooks/useFeatureFlags.ts` with two patterns: boolean flags using `useFeatureFlagEnabled("key")` and variant flags using `useFeatureFlagVariantKey("key")` with type casting

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • apps/web/hooks/useBeforeUnload.ts
📚 Learning: 2025-11-25T14:38:27.988Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/posthog-feature-flags.mdc:0-0
Timestamp: 2025-11-25T14:38:27.988Z
Learning: Applies to apps/web/hooks/useFeatureFlags.ts : All feature flag hooks should be defined in `apps/web/hooks/useFeatureFlags.ts`

Applied to files:

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

Applied to files:

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

Applied to files:

  • apps/web/hooks/useBeforeUnload.ts
📚 Learning: 2025-11-25T14:37:09.306Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/fullstack-workflow.mdc:0-0
Timestamp: 2025-11-25T14:37:09.306Z
Learning: Applies to apps/web/hooks/use*.ts : Use SWR hooks for client-side data fetching, with hooks stored in `apps/web/hooks/use*.ts` that return typed responses from GET API routes

Applied to files:

  • apps/web/hooks/useBeforeUnload.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
📚 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/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.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/BulkRunRules.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/BulkRunRules.tsx
📚 Learning: 2025-06-05T09:49:12.168Z
Learnt from: elie222
Repo: elie222/inbox-zero PR: 485
File: apps/web/app/(landing)/login/page.tsx:41-43
Timestamp: 2025-06-05T09:49:12.168Z
Learning: In Next.js App Router, components that use the `useSearchParams` hook require a Suspense boundary to handle the asynchronous nature of search parameter access. The Suspense wrapper is necessary and should not be removed when a component uses useSearchParams.

Applied to files:

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

Applied to files:

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

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
📚 Learning: 2025-11-25T14:40:13.649Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-11-25T14:40:13.649Z
Learning: Applies to **/*.{tsx,ts,jsx,js} : For API get requests to server, use the `swr` package with `useSWR` hook

Applied to files:

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

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
📚 Learning: 2025-12-17T11:18:06.818Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-17T11:18:06.818Z
Learning: Applies to apps/web/**/hooks/*.ts : Use SWR for client-side data fetching with type-safe response types

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.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 : 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/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx
📚 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/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx
📚 Learning: 2025-12-17T11:18:06.818Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-17T11:18:06.818Z
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/BulkProcessActivityLog.tsx
🧬 Code graph analysis (1)
apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx (5)
apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.ts (3)
  • bulkRunReducer (26-96)
  • initialBulkRunState (19-24)
  • getProgressMessage (98-115)
apps/web/hooks/useBeforeUnload.ts (1)
  • useBeforeUnload (7-21)
apps/web/utils/queue/ai-queue.ts (3)
  • resumeAiQueue (9-9)
  • pauseAiQueue (8-8)
  • clearAiQueue (10-10)
apps/web/store/ai-queue.ts (1)
  • clearAiQueueAtom (29-31)
apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx (1)
  • BulkProcessActivityLog (117-252)
⏰ 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). (4)
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: test
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Review for correctness
🔇 Additional comments (10)
apps/web/hooks/useBeforeUnload.ts (1)

7-21: LGTM!

The hook implementation is clean and follows best practices:

  • Proper cleanup of event listener on unmount or when enabled changes
  • Cross-browser compatibility addressed with both e.preventDefault() and e.returnValue = "" (fixing the Safari issue flagged in past reviews)
  • Follows hook naming conventions and placement guidelines
apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx (4)

68-78: Well-structured derived state addressing past concerns.

The introduction of isBusy = isProcessing || state.status === "processing" is a good improvement. This ensures useBeforeUnload protects both the initial fetch phase (when state.status === "processing" but queue is empty) and the active processing phase (when queue.size > 0).


80-118: Good error handling and validation.

The handleStart function properly:

  • Validates inputs before proceeding
  • Resumes the queue to prevent stuck state from previous runs
  • Has try/catch with user-friendly error toast
  • Dispatches RESET on failures to restore idle state

This addresses the past review concern about missing error handling.


225-233: Race condition from past reviews addressed.

The button visibility now checks state.status === "idle" && !isProcessing, which hides the button immediately when START is dispatched (since state.status becomes "processing"). This properly prevents double-clicks during the async gap before the queue is populated.


282-377: Solid batch processing implementation.

The onRun function is well-structured:

  • Returns abort function immediately while processing runs in background
  • Comprehensive error handling with user feedback
  • Rate limiting via sleep() to avoid Gmail API limits
  • Proper callbacks for state updates (onThreadsQueued, onComplete)
apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx (5)

1-16: LGTM!

Clean imports and type definitions. Proper use of "use client" directive and type keyword for type imports.


18-58: LGTM!

Well-structured presentation component with proper conditional rendering and accessibility.


60-101: LGTM!

Clean row component with appropriate status indicators and text truncation.


165-223: LGTM!

The effects properly manage activity log state with functional updates and appropriate dependencies. The threads dependency may trigger frequent updates, but this is necessary for real-time log accuracy.


225-252: LGTM!

Clean transformation of internal state to presentation props with proper status derivation.

Comment on lines +141 to +148
// Get message IDs from processed threads
const messageIds = Array.from(processedThreadIds)
.map((threadId) => {
const thread = threads.find((t) => t.id === threadId);
return thread?.messages?.[thread.messages.length - 1]?.id;
})
.filter((id): id is string => !!id)
.slice(-20); // Keep last 20
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 | 🟠 Major

Address the incomplete log issue from older threads.

As noted in the previous review, threads.find() returns undefined when bulk-processing threads not in the current inbox snapshot (lines 143-144, 174-175). This causes those entries to be skipped, resulting in an incomplete activity log.

Consider one of these approaches:

  1. Pass a complete thread map: Have the parent component provide a Map<threadId, thread> that includes all processed threads, not just the current inbox snapshot
  2. Fetch missing threads: When a threadId in processedThreadIds isn't found in threads, fetch it by ID
  3. Document the limitation: If this is intentional, add a comment explaining that the log only shows currently visible threads
🔎 Verification script to check impact
#!/bin/bash
# Description: Check how BulkProcessActivityLog is used and whether threads might be incomplete

# Find usages of BulkProcessActivityLog
rg -n "BulkProcessActivityLog" --type ts --type tsx -C 3

# Check if threads are filtered or limited before being passed
ast-grep --pattern $'<BulkProcessActivityLog
  threads={$_}
  $$$
/>'

Also applies to: 173-175

🤖 Prompt for AI Agents
In apps/web/app/(app)/[emailAccountId]/assistant/BulkProcessActivityLog.tsx
around lines 141-148 and 173-175, thread lookups using threads.find(...) can
return undefined for processedThreadIds that are not in the current inbox
snapshot, causing skipped log entries; fix by changing the component API to
accept a complete thread map (e.g., threadMap: Map<string, Thread>) from the
parent and use threadMap.get(threadId) as the primary lookup (falling back to
threads.find(...) only if the map is not provided), update call sites to supply
the full map (or document the limitation with a clear comment and prop name if
you choose not to), and add a brief comment in the file explaining the behavior.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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

307-374: Consider notifying user when hitting the iteration limit.

The loop is hard-capped at 100 iterations (line 308), which limits processing to ~2500 threads. If a user's date range contains more threads, processing will silently stop without notification. While this is a reasonable safeguard, users might be confused if not all emails are processed.

🔎 Suggested improvement
     for (let i = 0; i < 100; i++) {
       // ... existing logic ...
     }
 
+    if (nextPageToken) {
+      // Reached iteration limit with more pages remaining
+      console.warn("Bulk processing reached iteration limit");
+    }
+
     onComplete("success", totalProcessed);
   }

Alternatively, you could pass a status flag to onComplete to display a message like "Processed 2500 emails. Run again to continue."

📜 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 02d9714 and 46d6ca5.

📒 Files selected for processing (1)
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx (8 hunks)
🧰 Additional context used
📓 Path-based instructions (16)
**/*.{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/BulkRunRules.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/BulkRunRules.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/BulkRunRules.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
Use @/ path aliases for imports from project root
Follow tailwindcss patterns with prettier-plugin-tailwindcss class organization
Prefix client-side environment variables with NEXT_PUBLIC_
Leverage TypeScript inference for better developer experience with type exports from API routes

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.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/BulkRunRules.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/BulkRunRules.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/BulkRunRules.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/BulkRunRules.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/BulkRunRules.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/BulkRunRules.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/BulkRunRules.tsx
apps/web/**/app/**

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

Follow NextJS app router structure with (app) directory

Files:

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

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

apps/web/**/*.{tsx,jsx}: Prefer functional components with hooks over class components
Use shadcn/ui components when available
Follow consistent naming conventions with PascalCase for component names
Use LoadingContent component for async data with loading and error states
Use result?.serverError with toastError and toastSuccess for mutation error handling

Files:

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

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

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

Files:

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

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

Format code with Prettier

Files:

  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.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/BulkRunRules.tsx
🧠 Learnings (9)
📚 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/(app)/[emailAccountId]/assistant/BulkRunRules.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/BulkRunRules.tsx
📚 Learning: 2025-06-05T09:49:12.168Z
Learnt from: elie222
Repo: elie222/inbox-zero PR: 485
File: apps/web/app/(landing)/login/page.tsx:41-43
Timestamp: 2025-06-05T09:49:12.168Z
Learning: In Next.js App Router, components that use the `useSearchParams` hook require a Suspense boundary to handle the asynchronous nature of search parameter access. The Suspense wrapper is necessary and should not be removed when a component uses useSearchParams.

Applied to files:

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

Applied to files:

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

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
📚 Learning: 2025-11-25T14:40:13.649Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-11-25T14:40:13.649Z
Learning: Applies to **/*.{tsx,ts,jsx,js} : For API get requests to server, use the `swr` package with `useSWR` hook

Applied to files:

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

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
📚 Learning: 2025-12-17T11:18:06.830Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-17T11:18:06.830Z
Learning: Applies to apps/web/**/hooks/*.ts : Use SWR for client-side data fetching with type-safe response types

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.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 : Use SWR hooks for client-side data fetching, with hooks stored in `apps/web/hooks/use*.ts` that return typed responses from GET API routes

Applied to files:

  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.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). (2)
  • GitHub Check: test
  • GitHub Check: Review for correctness
🔇 Additional comments (6)
apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx (6)

1-43: LGTM!

The imports are well-organized, following project conventions. The "use client" directive is correctly placed for a component with event handlers. Using useThreads hook for data fetching aligns with the SWR pattern required by coding guidelines.


68-78: LGTM!

The derived state logic correctly addresses previous review concerns:

  • isBusy combines queue activity with processing status, covering the initial fetch phase
  • useBeforeUnload(isBusy) now protects the entire processing lifecycle including thread fetching
  • The separation of isProcessing (queue-based) and isBusy (includes status) provides appropriate granularity for different UI behaviors

80-118: LGTM!

The handleStart function properly addresses previous review concerns:

  • Input validation with user-friendly error toasts
  • resumeAiQueue() called before onRun to ensure queue isn't stale-paused
  • Try/catch with toast error and RESET dispatch for recovery
  • The onRun function returns the abort callback synchronously (before the async run() loop completes), so abortRef.current is available immediately

120-135: LGTM!

The pause/resume and stop handlers are well-implemented:

  • handlePauseResume correctly toggles queue state and dispatches corresponding actions
  • handleStop properly cleans up by clearing both the queue and atom state, then invoking the abort callback with optional chaining for safety

225-260: LGTM!

The button visibility logic correctly addresses previous review concerns:

  • "Process Emails" button shows for both idle and stopped states, allowing users to restart after stopping
  • Control buttons (Pause/Resume, Stop) are shown when isBusy, which covers both active processing and initial fetch
  • All buttons include the required type="button" attribute per coding guidelines

376-379: LGTM!

The abort mechanism is correctly implemented. By calling run() without await and returning abort immediately, the caller receives the abort handle synchronously while the async processing continues in the background. This addresses the previously flagged race condition concern.

@elie222 elie222 merged commit d0b6737 into main Dec 20, 2025
20 checks passed
@elie222 elie222 deleted the feat/bulk-process branch December 20, 2025 17:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant