Skip to content

Claude/prisma notification migration h qw rf#1170

Closed
don4of4 wants to merge 23 commits intoelie222:mainfrom
don4of4:claude/prisma-notification-migration-HQwRF
Closed

Claude/prisma notification migration h qw rf#1170
don4of4 wants to merge 23 commits intoelie222:mainfrom
don4of4:claude/prisma-notification-migration-HQwRF

Conversation

@don4of4
Copy link

@don4of4 don4of4 commented Jan 2, 2026

Add LLM-driven email expiration with per-account settings, Prisma models and cleanup jobs, integrate server-side bulk rule processing with concurrency and date filters, and increase AI queue concurrency to 30

Introduce expiration analysis and cleanup paths, including new Prisma models and API/UI for settings; refactor bulk processing to paginated, concurrent server-side execution with expiration updates; add Azure AI Foundry provider support; adjust OAuth prompts and queues; and update EmailProvider.getInboxMessages to accept { maxResults, after, before }.

📍Where to Start

Start with analyzeAndSetExpiration in apps/web/utils/expiration/analyze-expiration.ts, then review cleanupExpiredEmails in apps/web/utils/expiration/process-expired.ts and bulkProcessInboxEmails in apps/web/utils/ai/choose-rule/bulk-process-emails.ts.


📊 Macroscope summarized acb083f. 41 files reviewed, 41 issues evaluated, 21 issues filtered, 0 comments posted

🗂️ Filtered Issues

.github/workflows/build_and_publish_docker.yml — 0 comments posted, 3 evaluated, 1 filtered
  • line 68: Hardcoded NEXT_PUBLIC_BYPASS_PREMIUM_CHECKS=true in build-args permanently disables premium checks in all built images. This may cause unintended access to premium features or break licensing enforcement at runtime. [ Low confidence ]
apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx — 0 comments posted, 4 evaluated, 3 filtered
  • line 102: The onRun function is now called with a new fourth callback parameter (threadId) => dispatch({ type: "THREAD_COMPLETED", threadId }) to track completed threads. If the onRun function implementation was not updated to invoke this callback when threads complete, state.completedThreadIds will remain empty. This would cause the progress message to always show 0 completed emails, and handleStop would always report completedCount: 0 since it relies on state.completedThreadIds.size. [ Low confidence ]
  • line 130: The handleStop function captures state.completedThreadIds.size synchronously at the moment of invocation. If multiple THREAD_COMPLETED actions are dispatched in quick succession (e.g., from async queue callbacks) but haven't been processed by React's state batching when the user clicks Stop, the captured count may be slightly stale and not reflect threads that completed in the same event loop tick. [ Low confidence ]
  • line 369: The sleep time reduction from 5000ms/2000ms to 500ms/200ms combined with the concurrency increase to 30 in aiQueue may trigger Gmail API rate limits. The original comment explicitly stated the delay was to "avoid gmail api rate limits". Removing this protection while increasing parallelism could cause fetch failures on subsequent batches. [ Low confidence ]
apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRulesServerSide.tsx — 0 comments posted, 3 evaluated, 1 filtered
  • line 187: On line 187, Number(e.target.value) || 10 will reset concurrency to 10 if the user tries to enter 0, since 0 is falsy. While the min attribute is 1, a user could still type 0 and be surprised when it becomes 10. Consider using Number(e.target.value) with explicit validation or Number.isNaN() check instead. [ Low confidence ]
apps/web/app/(app)/[emailAccountId]/settings/ExpirationSection.tsx — 0 comments posted, 4 evaluated, 1 filtered
  • line 104: The toggleCategory callback has enabledCategories in its dependency array but captures a potentially stale value. If toggleCategory is called multiple times in quick succession before React re-renders, the current array on line 106 will use a stale enabledCategories value, causing toggle operations to be lost. [ Low confidence ]
apps/web/app/(app)/[emailAccountId]/settings/page.tsx — 0 comments posted, 2 evaluated, 2 filtered
  • line 62: The formSchema constant is referenced in zodResolver(formSchema) within ExpirationSection.tsx but is never defined or imported in the visible code. This will cause a ReferenceError at runtime when the component renders. [ Low confidence ]
  • line 62: The ExpirationSettingsResponse type declares settings can be null, but when prisma.emailExpirationSettings.findUnique returns null, the response { settings } will have settings: null. However, in ExpirationSection.tsx, the form uses data?.settings?.enabled ?? false which handles null correctly, BUT the CATEGORIES constant referenced in the form is never defined in the provided code. The form iterates over CATEGORIES.map((category) => ...) but CATEGORIES is not imported or defined, causing a runtime ReferenceError. [ Low confidence ]
apps/web/app/api/google/calendar/auth-url/route.ts — 0 comments posted, 1 evaluated, 1 filtered
  • line 25: Changing prompt from "consent" to "select_account" will cause Google to NOT issue a new refresh token for users who have previously authorized this application. With access_type: "offline", Google only issues a refresh token on initial consent or when consent is forced. If the user previously authorized the app, the callback will not receive a refresh token, causing the calendar integration to fail when the short-lived access token expires and the application attempts to refresh it. [ Low confidence ]
apps/web/app/api/google/webhook/route.ts — 0 comments posted, 1 evaluated, 1 filtered
  • line 18: The webhook now returns a 500 error when GOOGLE_PUBSUB_VERIFICATION_TOKEN is not configured, but existing deployments that worked without this token configured will now fail. This is a breaking change that could cause all Google webhooks to fail after deployment if the environment variable wasn't previously required. [ Low confidence ]
apps/web/app/api/user/expiration-settings/route.ts — 0 comments posted, 1 evaluated, 1 filtered
  • line 43: The POST handler in expiration-settings uses updateSettingsSchema.parse(body) which will throw a Zod error if validation fails. This error is not caught, so invalid requests will result in an unhandled exception rather than a proper 400 response with validation details. [ Low confidence ]
apps/web/utils/actions/ai-rule.ts — 0 comments posted, 1 evaluated, 1 filtered
  • line 651: The bulkProcessRulesAction always returns { success: true } regardless of whether bulkProcessInboxEmails succeeded or failed. Looking at bulkProcessInboxEmails, it wraps all processing in a try-catch that logs errors but doesn't re-throw them, causing the function to return undefined on failure. The action then proceeds to return { success: true }, misleading callers into thinking the bulk processing completed successfully when it may have failed entirely. [ Exceeded comment limit ]
apps/web/utils/actions/ai-rule.validation.ts — 0 comments posted, 3 evaluated, 1 filtered
  • line 19: No validation for conflicting options: when both daysBack and startDate/endDate are provided, the intended behavior is ambiguous. Downstream code must handle this priority, but the schema doesn't enforce mutual exclusivity which could lead to user confusion or inconsistent behavior depending on implementation. [ Low confidence ]
apps/web/utils/ai/choose-rule/ai-choose-rule.ts — 0 comments posted, 4 evaluated, 1 filtered
  • line 284: Logging modelOptions.model object instead of modelOptions.modelName string in the multi-rule start log. [ Low confidence ]
apps/web/utils/ai/choose-rule/bulk-process-emails.ts — 0 comments posted, 2 evaluated, 2 filtered
  • line 88: The logging statements in run-rules.ts reference a MODULE constant (e.g., module: MODULE) but the constant is not shown as defined or imported in the provided code. If MODULE is not defined elsewhere in the file, this will cause a ReferenceError at runtime when the logging code executes. [ Low confidence ]
  • line 99: The code calls emailProvider.getMessagesWithPagination() but the diff shows the previous implementation used emailProvider.getInboxMessages(maxEmails). If OutlookProvider (or GmailProvider) does not implement the new getMessagesWithPagination method with the expected signature returning { messages, nextPageToken }, this will throw a runtime error when attempting to process emails for accounts using that provider. [ Low confidence ]
apps/web/utils/auth.ts — 0 comments posted, 1 evaluated, 1 filtered
  • line 105: Changing prompt from "select_account consent" to "select_account" removes the explicit consent prompt during Google OAuth. This may cause issues where users who previously granted consent don't get prompted to re-consent when scopes change, potentially resulting in missing permissions if the app's required scopes have been updated since the user last authenticated. [ Low confidence ]
apps/web/utils/expiration/analyze-expiration.ts — 0 comments posted, 5 evaluated, 3 filtered
  • line 161: On line 161, settings.enabledCategories.includes(category) assumes enabledCategories is always a defined array. If this field is null, undefined, or not initialized in the database record, this will throw TypeError: Cannot read properties of undefined (reading 'includes'). [ Low confidence ]
  • line 167: On line 167, getDefaultExpirationDays(category, settings) is called with the Prisma settings object, but getDefaultExpirationDays expects an object with specific property names like notificationDays, newsletterDays, etc. If the Prisma model uses different column names (e.g., notification_days or camelCase variations), the function will ignore user settings and always fall back to category defaults. [ Low confidence ]
  • line 227: On line 227, the regex /@([^>]+)/ in extractDomain captures everything after @ until a > character, but doesn't account for email addresses with multiple @ symbols in display names (e.g., "user@display" <real@example.com>), which could extract the wrong domain. Also, addresses without @ will return an empty string, which may cause downstream issues if domain is required. [ Low confidence ]
apps/web/utils/gmail/message.ts — 0 comments posted, 1 evaluated, 1 filtered
  • line 317: When maxResults is undefined, queryBatchMessagesPages will loop indefinitely until all pages are exhausted (lines 317-320). For users with large inboxes, this could fetch millions of messages, causing memory exhaustion, rate limit errors, or extremely long execution times. The function lacks any safeguard like a maximum page count or total message limit. [ Low confidence ]

Summary by CodeRabbit

  • New Features

    • Email expiration and automatic cleanup with category-based rules and cleanup integration
    • Server-side bulk rule processing for improved scalability
    • Email expiration settings UI for users to configure retention policies by category
    • Azure AI Foundry LLM provider support
  • Improvements

    • Enhanced inbox filtering with date range support
    • Increased concurrency for AI and email processing queues
    • Simplified landing page navigation and layout
    • Improved Docker workflow with registry-based configuration and GHCR support
    • Enhanced logging and performance instrumentation across AI processing
  • Tests

    • Improved test infrastructure with logger injection for email provider initialization

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

don4of4 and others added 23 commits December 26, 2025 15:32
- Add Azure AI Foundry as LLM provider option (azure-foundry)
- Enhanced bulk email processing with:
  - Parallel processing with configurable concurrency (default: 5)
  - Streaming pagination (100 emails per page)
  - Date filtering (after/before parameters)
  - Skip already processed emails option
  - Process oldest first option for chronological processing
- Updated dependencies in pnpm-lock.yaml

🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier)

Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
- Uses standard Docker Buildx (no Depot dependency)
- Builds for linux/amd64 and linux/arm64
- Publishes to ghcr.io/don4of4/inbox-zero
- Configures NEXT_PUBLIC_BASE_URL for self-hosted deployment
- Enables BYPASS_PREMIUM_CHECKS for self-hosted use

🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier)

Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
- Remove repository check that blocked fork builds
- Replace Depot with standard Docker Buildx
- Use github.repository for dynamic image naming
- Remove Docker Hub publishing (GHCR only)
- Add build caching for faster subsequent builds
- Configure for inbox.donaldscott.com deployment

🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier)

Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
TrueNAS target is x86_64 - no need for multi-arch.
Cuts build time roughly in half.

🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier)

Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Remove marketing sections: Pricing, Testimonials, Awards, FinalCTA, BrandScroller
- Simplify footer from 50+ links to 5 essentials (Docs, GitHub, Discord, Terms, Privacy)
- Change CTA buttons to single "Sign In" button
- Update copyright to "Self-hosted instance"

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

Co-Authored-By: Claude <noreply@anthropic.com>
Previously, if GOOGLE_PUBSUB_VERIFICATION_TOKEN was not set, the webhook
would accept all requests without verification. This change rejects
requests when the token is not configured, preventing unauthenticated
webhook abuse.
The getProgressMessage function was refactored to use completedThreadIds
from state instead of a remainingCount parameter. Updated tests to:
- Remove the unused second parameter
- Set up completedThreadIds in test state
Changed `prompt: "consent"` to `prompt: "select_account"` in three places:
- Main Google OAuth config (auth.ts)
- Google account linking (linking/auth-url/route.ts)
- Google calendar auth (calendar/auth-url/route.ts)

Previously, the consent screen was shown on every login even when no new
permissions were requested. Now it only shows when permissions change.
…ture

This plan details how to implement automatic cleanup of "aged out" emails
(notifications, newsletters, promotions) with configurable expiration periods.

Key features:
- Per-category expiration settings (Notifications: 7d, Newsletters: 30d, etc.)
- Daily cron job via QStash for processing
- Archive + "Inbox Zero/Expired" label for audit trail
- Settings UI for user configuration
- Optional AI-based custom expiration rules
- Integration with existing cleanup infrastructure
Major changes to the implementation plan:

- LLM analyzes email content to extract relevant dates
  (package delivery, events, sale end dates)
- Per-email expiresAt stored on EmailMessage model
- Two processing paths:
  1. Real-time: Hook after runRules() in webhook processing
  2. Batch backfill: Cron for emails without expiration set
- Cleanup cron finds emails past expiresAt and archives them
- Stores LLM reasoning for transparency
- Falls back to category defaults when no date found
- Hooks into existing cron pattern (/api/cron/expiration/*)

Example transformations:
- "Package arrives Nov 5" → expiresAt: Nov 12
- "Meeting tomorrow" → expiresAt: day after meeting
- "Sale ends tonight" → expiresAt: tomorrow
Key changes:
- Phase 4: Use existing /api/watch/all cron for cleanup instead of new endpoints
- Remove Phase 7: No new cron schedules needed
- Update file structure: Clarify new files vs files to modify
- Cleanup runs in existing cron, backfill uses existing bulk button
Phase 1: Schema changes
- Add expiresAt, expiredAt, expirationReason to EmailMessage
- Add EmailExpirationSettings model for user configuration
- Add ExpiredEmailLog model for audit trail
- Add composite index for expiration queries

Phase 2: Core utilities
- Add utils/expiration/categories.ts for category detection
- Add utils/expiration/process-expired.ts for cleanup logic
- Add "expired" label to inboxZeroLabels

Phase 3: Hook into existing cron
- Modify /api/watch/all to call cleanupExpiredEmails()
- Graceful error handling - cleanup failures don't break watch

Note: LLM-based expiration analysis (Phase 2 of plan) not yet implemented.
This commit provides the infrastructure for cleanup; expiration dates
need to be set by a future analyzeExpiration() implementation.
Adds the "smart" part of email expiration - LLM analyzes email content
to extract relevant dates and set context-aware expiration.

New file:
- utils/expiration/analyze-expiration.ts
  - analyzeExpirationWithLLM(): Calls LLM to extract dates from content
  - analyzeAndSetExpiration(): Main entry point that checks settings,
    detects category, runs LLM analysis, and stores result

Hooks into existing flows:
- process-history-item.ts: Runs expiration analysis in background via
  after() for new incoming emails
- bulk-process-emails.ts: Runs expiration analysis after runRules()
  for each message during bulk processing

Examples of LLM analysis:
- "Your package arrives November 5th" -> expires Nov 12 (7 days after)
- "Event tomorrow at 3pm" -> expires day after event
- "Weekly newsletter" -> expires in 30 days (default)
- Add API endpoint: /api/user/expiration-settings (GET/POST)
- Add ExpirationSection.tsx component with:
  - Master toggle to enable/disable auto-expiration
  - Option to apply "Expired" label when archiving
  - Per-category toggles and default day settings
  - AI analyzes email content for specific dates as fallback
- Integrate into Settings page under "Email Account" tab
Create migration to add:
- EmailExpirationSettings table for per-account expiration preferences
- ExpiredEmailLog table for audit logging of archived emails
- expiresAt, expiredAt, expirationReason columns to EmailMessage
- Composite index for efficient expiration queries
The content-type header is not part of ParsedMessageHeaders type.
Check attachments for calendar mimeType or .ics/.ical extensions instead.
- Replace postRequest with native fetch API
- Use registerProps instead of spreading register result
- Add required name prop to Input component
- Fix Prisma import path in bulk-process-emails.ts
- Update OutlookProvider.getInboxMessages to match EmailProvider interface
- Fix getInboxMessages call in fetch.ts to use options object
- Add createTestLogger helper for e2e tests
- Add logger to createEmailProvider calls in e2e tests
- Fix getInboxMessages calls to use options object
- Add exclude property to group items in tests
The API middleware requires this header for authentication.
Without it, requests return 403 Forbidden.
@vercel
Copy link

vercel bot commented Jan 2, 2026

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

A member of the Team first needs to authorize it.

@CLAassistant
Copy link

CLAassistant commented Jan 2, 2026

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
0 out of 2 committers have signed the CLA.

❌ don4of4
❌ claude
You have signed the CLA already but the status is still pending? Let us recheck it.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 2, 2026

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

This PR introduces an email expiration feature with LLM-powered analysis, refactors email provider APIs to support date-range filtering, simplifies landing pages by removing sections, adds Azure Foundry LLM support, increases queue concurrency, updates Docker CI for GHCR, and instruments logging throughout AI processing pipelines.

Changes

Cohort / File(s) Summary
Email Expiration Feature — Database & Migrations
prisma/migrations/20251229030000_add_email_expiration/migration.sql, prisma/schema.prisma
Adds EmailExpirationSettings and ExpiredEmailLog models; extends EmailMessage with expiresAt, expiredAt, expirationReason; adds foreign keys and indexes for expiration queries.
Email Expiration Feature — API Endpoints
app/api/user/expiration-settings/route.ts, app/api/watch/all/route.ts
New GET/POST handlers for expiration settings management; integrates cleanupExpiredEmails into watch flow with result aggregation.
Email Expiration Feature — Expiration Analysis & Cleanup
utils/expiration/categories.ts, utils/expiration/analyze-expiration.ts, utils/expiration/process-expired.ts
Detects email categories (notifications, newsletters, marketing, social, calendar), analyzes expiration via LLM, persists expiresAt/expirationReason, batch-cleans expired emails with archiving and logging.
Email Expiration Feature — UI & Integration
app/(app)/[emailAccountId]/settings/ExpirationSection.tsx, app/(app)/[emailAccountId]/settings/page.tsx, webhook/process-history-item.ts
New settings form for expiration configuration with per-category day inputs; hooks expiration analysis into webhook message processing via background task.
Bulk Processing Enhancements
app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.ts, app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.test.ts, app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
Tracks completed threads via Set instead of derived counts; adds THREAD_COMPLETED action; adjusts progress reporting to use state.completedThreadIds; reduces batch delay from 5s to 500ms.
Bulk Processing — Server-Side Component
app/(app)/[emailAccountId]/assistant/BulkRunRulesServerSide.tsx, app/(app)/[emailAccountId]/assistant/ProcessRules.tsx
New server-side bulk processing UI with modal dialog for time range/concurrency/skip options; calls bulkProcessRulesAction; integrated alongside existing BulkRunRules.
Bulk Processing — Enhanced Algorithm
utils/ai/choose-rule/bulk-process-emails.ts, utils/actions/ai-rule.ts, utils/actions/ai-rule.validation.ts
Refactors bulkProcessInboxEmails to support pagination, date filtering (after/before), concurrency control, skip-already-processed, and oldest-first sorting; integrates expiration analysis per-message; new bulkProcessRulesAction server action.
Email Provider API Refactoring
utils/email/types.ts, utils/email/google.ts, utils/email/microsoft.ts, utils/gmail/message.ts
getInboxMessages signature changed from primitive maxResults to options object with maxResults, after, before; implementations updated with date-filtered queries and paginated fetches.
E2E Tests — Logger Injection
__tests__/e2e/helpers.ts, __tests__/e2e/cold-email/*.test.ts, __tests__/e2e/labeling/*.test.ts, __tests__/e2e/outlook-*.test.ts, __tests__/e2e/gmail-operations.test.ts, __tests__/e2e/drafting/microsoft-drafting.test.ts
New createTestLogger() helper; logger instance injected into all createEmailProvider calls; getInboxMessages calls updated to use options object.
Logging & Instrumentation
utils/ai/choose-rule/ai-choose-rule.ts, utils/ai/choose-rule/run-rules.ts, utils/reply-tracker/handle-conversation-status.ts
Adds timing instrumentation (start time, duration in ms/sec) around AI calls and conversation status determination; logs rule counts and model names.
Queue & Concurrency
utils/queue/ai-queue.ts, utils/queue/email-action-queue.ts, utils/queue/email-actions.ts
Increases AI queue from 3 to 30 concurrency; email action queue from 1 to 5; adds clearAiQueue and isAiQueuePaused exports; runAiRules now accepts optional onThreadCompleted callback with guaranteed cleanup.
LLM Provider — Azure Foundry
env.ts, utils/llms/config.ts, utils/llms/model.ts
Adds azure-foundry to llmProviderEnum; new env vars (AZURE_FOUNDRY_RESOURCE_NAME, AZURE_FOUNDRY_API_KEY, AZURE_FOUNDRY_API_VERSION); selectModel handles Azure Foundry via Anthropic-compatible baseURL.
Auth & OAuth
utils/auth.ts, app/api/google/calendar/auth-url/route.ts, app/api/google/linking/auth-url/route.ts, app/api/google/webhook/route.ts
OAuth prompt changed from "consent" to "select_account"; webhook verification token now validated with explicit guard and early rejection on misconfiguration.
Landing Page Simplification
app/(landing)/page.tsx, app/(landing)/home/CTAButtons.tsx, app/(landing)/home/Footer.tsx, components/new-landing/sections/Footer.tsx
Removes Testimonials, Pricing, Awards, FinalCTA, BrandScroller components; CTA simplified to single Sign In button; footer restructured to dynamic link generation without separate section headers.
Configuration & Build
.github/workflows/build_and_publish_docker.yml, next.config.ts, utils/actions/clean.ts, utils/label.ts, utils/ai/report/fetch.ts
Docker CI updated to GHCR with registry variables, multi-step Buildx setup, dropped Depot; Next.js config ignores build-time type errors; cleanInbox gated by NEXT_PUBLIC_BYPASS_PREMIUM_CHECKS; new "Expired" label added; inbox fetch updated to options API.
Test Data & Validation
utils/group/find-matching-group.test.ts
Test objects updated to include explicit exclude: false flag on group items.

Sequence Diagram(s)

sequenceDiagram
    participant W as Webhook<br/>(history item)
    participant MP as Message<br/>Processor
    participant EA as Expiration<br/>Analysis
    participant LLM as LLM
    participant DB as Database
    participant L as Logger

    W->>MP: process history item
    Note over MP: Check hasAiAccess & inbox
    alt inbox message
        MP->>EA: analyzeAndSetExpiration()<br/>(background)
        Note over EA: Fetch expiration settings
        alt settings enabled
            EA->>EA: Detect category
            alt category detected & enabled
                EA->>LLM: Analyze expiration<br/>(with system/message prompts)
                LLM-->>EA: { shouldExpire, expiresAt, reason }
                EA->>DB: Upsert EmailMessage<br/>(expiresAt, expirationReason)
                EA->>L: Log success
            else no expirable category
                EA->>L: Log skip (no category)
            end
        else settings disabled
            EA->>L: Log skip (disabled)
        end
    end
    MP-->>W: return (non-blocking)
Loading
sequenceDiagram
    participant UI as BulkRunRulesServerSide<br/>(UI)
    participant Action as bulkProcessRulesAction<br/>(Server Action)
    participant Bulk as bulkProcessInboxEmails
    participant Gmail as Gmail API
    participant Rules as Rule Matching
    participant Exp as Expiration Analysis
    participant DB as Database

    UI->>Action: Call with { daysBack, maxEmails, concurrency, ... }
    Action->>Bulk: Invoke with computed date filters
    loop Pages (paginated fetch)
        Bulk->>Gmail: queryBatchMessagesPages(after, before)
        Gmail-->>Bulk: Messages (paginated)
        Note over Bulk: Deduplicate by thread<br/>(cross-page)
        alt skipAlreadyProcessed
            Bulk->>DB: Filter out ExecutedRule matches
        end
        Note over Bulk: Sort by date (processOldestFirst)
        loop Batches (concurrency)
            Bulk->>Rules: Run rules per message
            Rules-->>Bulk: Action result
            alt hasAiAccess
                Bulk->>Exp: analyzeAndSetExpiration()
                Exp-->>DB: Persist expiresAt
            end
        end
    end
    Action-->>UI: { success: true }
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • Fix expiration type #634: Overlaps on expiresAt/expiredAt timestamp plumbing and how expiration timestamps are converted and passed through client helpers; related infrastructure changes for the same expiration feature.
  • Outlook support #537: Directly related—both expand the EmailProvider abstraction and refactor getInboxMessages signatures and provider-based call sites across email implementations.
  • Onboarding process emails #1023: Related—both modify bulkProcessInboxEmails and add/extend skip and filter parameters for bulk email processing; overlapping rule-running APIs.

Poem

🐰 Whiskers twitch with glee,
Emails fade like morning dew,
Azure skies now gleam,
Bulk rules race at speed,
Inbox zero's fleeting dream!

✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent 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 d139179 and acb083f.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (60)
  • .github/workflows/build_and_publish_docker.yml
  • apps/web/__tests__/e2e/cold-email/google-cold-email.test.ts
  • apps/web/__tests__/e2e/cold-email/microsoft-cold-email.test.ts
  • apps/web/__tests__/e2e/drafting/microsoft-drafting.test.ts
  • apps/web/__tests__/e2e/gmail-operations.test.ts
  • apps/web/__tests__/e2e/helpers.ts
  • apps/web/__tests__/e2e/labeling/gmail-thread-label-removal.test.ts
  • apps/web/__tests__/e2e/labeling/google-labeling.test.ts
  • apps/web/__tests__/e2e/labeling/helpers.ts
  • apps/web/__tests__/e2e/labeling/microsoft-labeling.test.ts
  • apps/web/__tests__/e2e/labeling/microsoft-thread-category-removal.test.ts
  • apps/web/__tests__/e2e/outlook-draft-read-status.test.ts
  • apps/web/__tests__/e2e/outlook-operations.test.ts
  • apps/web/__tests__/e2e/outlook-query-parsing.test.ts
  • apps/web/__tests__/e2e/outlook-search.test.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRulesServerSide.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/ProcessRules.tsx
  • apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.test.ts
  • apps/web/app/(app)/[emailAccountId]/assistant/bulk-run-rules-reducer.ts
  • apps/web/app/(app)/[emailAccountId]/settings/ExpirationSection.tsx
  • apps/web/app/(app)/[emailAccountId]/settings/page.tsx
  • apps/web/app/(landing)/home/CTAButtons.tsx
  • apps/web/app/(landing)/home/Footer.tsx
  • apps/web/app/(landing)/page.tsx
  • apps/web/app/api/google/calendar/auth-url/route.ts
  • apps/web/app/api/google/linking/auth-url/route.ts
  • apps/web/app/api/google/webhook/route.ts
  • apps/web/app/api/user/expiration-settings/route.ts
  • apps/web/app/api/watch/all/route.ts
  • apps/web/components/new-landing/sections/Footer.tsx
  • apps/web/env.ts
  • apps/web/next.config.ts
  • apps/web/prisma/migrations/20251229030000_add_email_expiration/migration.sql
  • apps/web/prisma/schema.prisma
  • apps/web/utils/actions/ai-rule.ts
  • apps/web/utils/actions/ai-rule.validation.ts
  • apps/web/utils/actions/clean.ts
  • apps/web/utils/ai/choose-rule/ai-choose-rule.ts
  • apps/web/utils/ai/choose-rule/bulk-process-emails.ts
  • apps/web/utils/ai/choose-rule/run-rules.ts
  • apps/web/utils/ai/report/fetch.ts
  • apps/web/utils/auth.ts
  • apps/web/utils/email/google.ts
  • apps/web/utils/email/microsoft.ts
  • apps/web/utils/email/types.ts
  • apps/web/utils/expiration/analyze-expiration.ts
  • apps/web/utils/expiration/categories.ts
  • apps/web/utils/expiration/process-expired.ts
  • apps/web/utils/gmail/message.ts
  • apps/web/utils/group/find-matching-group.test.ts
  • apps/web/utils/label.ts
  • apps/web/utils/llms/config.ts
  • apps/web/utils/llms/model.ts
  • apps/web/utils/queue/ai-queue.ts
  • apps/web/utils/queue/email-action-queue.ts
  • apps/web/utils/queue/email-actions.ts
  • apps/web/utils/reply-tracker/handle-conversation-status.ts
  • apps/web/utils/webhook/process-history-item.ts
  • docs/plans/email-expiration-cleanup.md

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.

@don4of4 don4of4 closed this Jan 2, 2026
@don4of4
Copy link
Author

don4of4 commented Jan 2, 2026

Disregard -- unintentional PR.

Copy link
Contributor

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

Choose a reason for hiding this comment

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

12 issues found across 61 files

Prompt for AI agents (all issues)

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


<file name="docs/plans/email-expiration-cleanup.md">

<violation number="1" location="docs/plans/email-expiration-cleanup.md:1037">
P1: The `updateConfig` function is called but never defined in the component. This code example would cause a runtime error if used as-is.</violation>
</file>

<file name="apps/web/app/api/google/calendar/auth-url/route.ts">

<violation number="1" location="apps/web/app/api/google/calendar/auth-url/route.ts:25">
P2: Changing `prompt` from `&quot;consent&quot;` to `&quot;select_account&quot;` may prevent refresh tokens from being returned on re-authorization. Since `access_type: &quot;offline&quot;` is set, the application needs refresh tokens for long-term access. Google only issues refresh tokens on the initial consent grant; subsequent auth flows with `&quot;select_account&quot;` won&#39;t return one. If this is intentional (e.g., for better UX when users already have valid tokens), consider adding a comment explaining this. Otherwise, consider using `&quot;consent select_account&quot;` to get both behaviors.</violation>
</file>

<file name="apps/web/utils/ai/choose-rule/ai-choose-rule.ts">

<violation number="1" location="apps/web/utils/ai/choose-rule/ai-choose-rule.ts:10">
P2: Per codebase logging conventions, helper functions should receive the logger as a parameter from the route/action rather than using `createScopedLogger`. This loses request context (requestId, userId, emailAccountId) that would be available from middleware. Consider adding a `logger: Logger` parameter to these functions and passing it from the caller.</violation>
</file>

<file name="apps/web/next.config.ts">

<violation number="1" location="apps/web/next.config.ts:18">
P1: Disabling TypeScript build errors (`ignoreBuildErrors: true`) allows type errors to reach production, which can cause runtime failures. Instead of bypassing type checking, the underlying AI SDK version mismatch should be resolved by aligning dependency versions or adding proper type declarations. This is a workaround that masks technical debt.</violation>
</file>

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

<violation number="1" location="apps/web/utils/auth.ts:105">
P1: Removing `consent` from the prompt will prevent Google from returning refresh tokens for returning users. When `accessType: &quot;offline&quot;` is set, Google only returns a refresh token on the first authorization or when the user re-consents. Without `consent` in the prompt, if a user&#39;s existing refresh token becomes invalid and they need to re-authenticate, they won&#39;t receive a new refresh token, breaking the app&#39;s ability to maintain long-term access.</violation>
</file>

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

<violation number="1" location="apps/web/app/(app)/[emailAccountId]/assistant/BulkRunRulesServerSide.tsx:49">
P2: The `data` from `useThreads` is fetched but never used beyond a truthy check. This causes an unnecessary API call to fetch inbox threads. Consider removing this hook if only premium/loading state is needed, or use the data if it&#39;s intended to show thread counts.</violation>
</file>

<file name="apps/web/app/(landing)/home/Footer.tsx">

<violation number="1" location="apps/web/app/(landing)/home/Footer.tsx:67">
P2: Social links are missing `target={item.target}`. The social items define `target: &quot;_blank&quot;` but it&#39;s not applied to the Link component, unlike the main navigation items above. This will cause social links to open in the same tab instead of a new tab.</violation>
</file>

<file name="apps/web/app/api/google/linking/auth-url/route.ts">

<violation number="1" location="apps/web/app/api/google/linking/auth-url/route.ts:21">
P2: Changing `prompt` from `&quot;consent&quot;` to `&quot;select_account&quot;` may prevent refresh tokens from being returned. With `access_type: &quot;offline&quot;`, Google only provides a refresh token on the first authorization or when consent is explicitly forced. Returning users selecting an already-authorized account won&#39;t receive a new refresh token, which could cause authentication failures when the access token expires.

Consider using `prompt: &quot;consent select_account&quot;` to allow account selection while still forcing consent, or ensure your token handling logic accounts for cases where no refresh token is returned.</violation>
</file>

<file name="apps/web/utils/actions/ai-rule.ts">

<violation number="1" location="apps/web/utils/actions/ai-rule.ts:651">
P2: The action always returns `{ success: true }` even if `bulkProcessInboxEmails` fails, since that function catches and swallows errors internally. Consider wrapping the call in try-catch to return appropriate error status, or returning processing statistics to give callers meaningful feedback.</violation>
</file>

<file name="apps/web/utils/expiration/analyze-expiration.ts">

<violation number="1" location="apps/web/utils/expiration/analyze-expiration.ts:116">
P2: LLM-returned `expiresAt` string is converted to Date without validation. If the LLM returns a malformed date string, `new Date()` creates an Invalid Date that propagates to the database. Consider validating the parsed date before returning it.</violation>
</file>

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

<violation number="1" location="apps/web/app/(app)/[emailAccountId]/settings/ExpirationSection.tsx:71">
P1: The SWR fetch doesn&#39;t include the `EMAIL_ACCOUNT_HEADER` that is used in the POST request. If the API requires this header for account-specific data, the GET will return incorrect results. Consider using a custom fetcher or including the header.</violation>

<violation number="2" location="apps/web/app/(app)/[emailAccountId]/settings/ExpirationSection.tsx:226">
P2: Input is missing the `error` prop. Validation errors from the zod schema (min 1, max 365/30) won&#39;t be displayed to users.</violation>
</file>

Since this is your first cubic review, here's how it works:

  • cubic automatically reviews your code and comments on bugs and improvements
  • Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
  • Ask questions if you need clarification on any suggestion

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

key={category.id}
category={category}
config={configs.find(c => c.category === category.id)}
onUpdate={(config) => updateConfig(category.id, config)}
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Jan 2, 2026

Choose a reason for hiding this comment

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

P1: The updateConfig function is called but never defined in the component. This code example would cause a runtime error if used as-is.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/plans/email-expiration-cleanup.md, line 1037:

<comment>The `updateConfig` function is called but never defined in the component. This code example would cause a runtime error if used as-is.</comment>

<file context>
@@ -0,0 +1,1237 @@
+                key={category.id}
+                category={category}
+                config={configs.find(c =&gt; c.category === category.id)}
+                onUpdate={(config) =&gt; updateConfig(category.id, config)}
+              /&gt;
+            ))}
</file context>
Fix with Cubic

scope: CALENDAR_SCOPES,
state,
prompt: "consent",
prompt: "select_account",
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Jan 2, 2026

Choose a reason for hiding this comment

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

P2: Changing prompt from "consent" to "select_account" may prevent refresh tokens from being returned on re-authorization. Since access_type: "offline" is set, the application needs refresh tokens for long-term access. Google only issues refresh tokens on the initial consent grant; subsequent auth flows with "select_account" won't return one. If this is intentional (e.g., for better UX when users already have valid tokens), consider adding a comment explaining this. Otherwise, consider using "consent select_account" to get both behaviors.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/app/api/google/calendar/auth-url/route.ts, line 25:

<comment>Changing `prompt` from `&quot;consent&quot;` to `&quot;select_account&quot;` may prevent refresh tokens from being returned on re-authorization. Since `access_type: &quot;offline&quot;` is set, the application needs refresh tokens for long-term access. Google only issues refresh tokens on the initial consent grant; subsequent auth flows with `&quot;select_account&quot;` won&#39;t return one. If this is intentional (e.g., for better UX when users already have valid tokens), consider adding a comment explaining this. Otherwise, consider using `&quot;consent select_account&quot;` to get both behaviors.</comment>

<file context>
@@ -22,7 +22,7 @@ const getAuthUrl = ({ emailAccountId }: { emailAccountId: string }) =&gt; {
     scope: CALENDAR_SCOPES,
     state,
-    prompt: &quot;consent&quot;,
+    prompt: &quot;select_account&quot;,
   });
 
</file context>
Fix with Cubic

import { getUserInfoPrompt, getUserRulesPrompt } from "@/utils/ai/helpers";
import { createScopedLogger } from "@/utils/logger";

const logger = createScopedLogger("ai-choose-rule");
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Jan 2, 2026

Choose a reason for hiding this comment

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

P2: Per codebase logging conventions, helper functions should receive the logger as a parameter from the route/action rather than using createScopedLogger. This loses request context (requestId, userId, emailAccountId) that would be available from middleware. Consider adding a logger: Logger parameter to these functions and passing it from the caller.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/utils/ai/choose-rule/ai-choose-rule.ts, line 10:

<comment>Per codebase logging conventions, helper functions should receive the logger as a parameter from the route/action rather than using `createScopedLogger`. This loses request context (requestId, userId, emailAccountId) that would be available from middleware. Consider adding a `logger: Logger` parameter to these functions and passing it from the caller.</comment>

<file context>
@@ -5,6 +5,9 @@ import { isDefined, type EmailForLLM } from &quot;@/utils/types&quot;;
 import { getUserInfoPrompt, getUserRulesPrompt } from &quot;@/utils/ai/helpers&quot;;
+import { createScopedLogger } from &quot;@/utils/logger&quot;;
+
+const logger = createScopedLogger(&quot;ai-choose-rule&quot;);
 
 type GetAiResponseOptions = {
</file context>
Fix with Cubic

reactStrictMode: true,
typescript: {
// Skip type checking during build (type errors in AI SDK version mismatches)
ignoreBuildErrors: true,
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Jan 2, 2026

Choose a reason for hiding this comment

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

P1: Disabling TypeScript build errors (ignoreBuildErrors: true) allows type errors to reach production, which can cause runtime failures. Instead of bypassing type checking, the underlying AI SDK version mismatch should be resolved by aligning dependency versions or adding proper type declarations. This is a workaround that masks technical debt.

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

<comment>Disabling TypeScript build errors (`ignoreBuildErrors: true`) allows type errors to reach production, which can cause runtime failures. Instead of bypassing type checking, the underlying AI SDK version mismatch should be resolved by aligning dependency versions or adding proper type declarations. This is a workaround that masks technical debt.</comment>

<file context>
@@ -13,6 +13,10 @@ const withMDX = nextMdx({
   reactStrictMode: true,
+  typescript: {
+    // Skip type checking during build (type errors in AI SDK version mismatches)
+    ignoreBuildErrors: true,
+  },
   output: process.env.DOCKER_BUILD === &quot;true&quot; ? &quot;standalone&quot; : undefined,
</file context>
Fix with Cubic

scope: [...GMAIL_SCOPES],
accessType: "offline",
prompt: "select_account consent",
prompt: "select_account",
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Jan 2, 2026

Choose a reason for hiding this comment

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

P1: Removing consent from the prompt will prevent Google from returning refresh tokens for returning users. When accessType: "offline" is set, Google only returns a refresh token on the first authorization or when the user re-consents. Without consent in the prompt, if a user's existing refresh token becomes invalid and they need to re-authenticate, they won't receive a new refresh token, breaking the app's ability to maintain long-term access.

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

<comment>Removing `consent` from the prompt will prevent Google from returning refresh tokens for returning users. When `accessType: &quot;offline&quot;` is set, Google only returns a refresh token on the first authorization or when the user re-consents. Without `consent` in the prompt, if a user&#39;s existing refresh token becomes invalid and they need to re-authenticate, they won&#39;t receive a new refresh token, breaking the app&#39;s ability to maintain long-term access.</comment>

<file context>
@@ -102,7 +102,7 @@ export const betterAuthConfig = betterAuth({
       scope: [...GMAIL_SCOPES],
       accessType: &quot;offline&quot;,
-      prompt: &quot;select_account consent&quot;,
+      prompt: &quot;select_account&quot;,
       disableIdTokenSignIn: true,
     },
</file context>
Fix with Cubic

access_type: "offline",
scope: [...new Set([...SCOPES, "openid", "email"])].join(" "),
prompt: "consent",
prompt: "select_account",
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Jan 2, 2026

Choose a reason for hiding this comment

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

P2: Changing prompt from "consent" to "select_account" may prevent refresh tokens from being returned. With access_type: "offline", Google only provides a refresh token on the first authorization or when consent is explicitly forced. Returning users selecting an already-authorized account won't receive a new refresh token, which could cause authentication failures when the access token expires.

Consider using prompt: "consent select_account" to allow account selection while still forcing consent, or ensure your token handling logic accounts for cases where no refresh token is returned.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/app/api/google/linking/auth-url/route.ts, line 21:

<comment>Changing `prompt` from `&quot;consent&quot;` to `&quot;select_account&quot;` may prevent refresh tokens from being returned. With `access_type: &quot;offline&quot;`, Google only provides a refresh token on the first authorization or when consent is explicitly forced. Returning users selecting an already-authorized account won&#39;t receive a new refresh token, which could cause authentication failures when the access token expires.

Consider using `prompt: &quot;consent select_account&quot;` to allow account selection while still forcing consent, or ensure your token handling logic accounts for cases where no refresh token is returned.</comment>

<file context>
@@ -18,7 +18,7 @@ const getAuthUrl = ({ userId }: { userId: string }) =&gt; {
     access_type: &quot;offline&quot;,
     scope: [...new Set([...SCOPES, &quot;openid&quot;, &quot;email&quot;])].join(&quot; &quot;),
-    prompt: &quot;consent&quot;,
+    prompt: &quot;select_account&quot;,
     state,
   });
</file context>
Fix with Cubic

processOldestFirst,
});

return { success: true };
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Jan 2, 2026

Choose a reason for hiding this comment

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

P2: The action always returns { success: true } even if bulkProcessInboxEmails fails, since that function catches and swallows errors internally. Consider wrapping the call in try-catch to return appropriate error status, or returning processing statistics to give callers meaningful feedback.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/utils/actions/ai-rule.ts, line 651:

<comment>The action always returns `{ success: true }` even if `bulkProcessInboxEmails` fails, since that function catches and swallows errors internally. Consider wrapping the call in try-catch to return appropriate error status, or returning processing statistics to give callers meaningful feedback.</comment>

<file context>
@@ -579,3 +581,73 @@ export const generateRulesPromptAction = actionClient
+        processOldestFirst,
+      });
+
+      return { success: true };
+    },
+  );
</file context>
Fix with Cubic


return {
shouldExpire,
expiresAt: expiresAt ? new Date(expiresAt) : null,
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Jan 2, 2026

Choose a reason for hiding this comment

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

P2: LLM-returned expiresAt string is converted to Date without validation. If the LLM returns a malformed date string, new Date() creates an Invalid Date that propagates to the database. Consider validating the parsed date before returning it.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/utils/expiration/analyze-expiration.ts, line 116:

<comment>LLM-returned `expiresAt` string is converted to Date without validation. If the LLM returns a malformed date string, `new Date()` creates an Invalid Date that propagates to the database. Consider validating the parsed date before returning it.</comment>

<file context>
@@ -0,0 +1,229 @@
+
+    return {
+      shouldExpire,
+      expiresAt: expiresAt ? new Date(expiresAt) : null,
+      reason,
+    };
</file context>
Fix with Cubic

</div>

<div className="flex items-center gap-2">
<Input
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Jan 2, 2026

Choose a reason for hiding this comment

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

P2: Input is missing the error prop. Validation errors from the zod schema (min 1, max 365/30) won't be displayed to users.

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

<comment>Input is missing the `error` prop. Validation errors from the zod schema (min 1, max 365/30) won&#39;t be displayed to users.</comment>

<file context>
@@ -0,0 +1,257 @@
+                          &lt;/div&gt;
+
+                          &lt;div className=&quot;flex items-center gap-2&quot;&gt;
+                            &lt;Input
+                              type=&quot;number&quot;
+                              name={category.field}
</file context>
Fix with Cubic


export function ExpirationSection() {
const { emailAccountId } = useAccount();
const { data, isLoading, mutate } = useSWR<ExpirationSettingsResponse>(
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Jan 2, 2026

Choose a reason for hiding this comment

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

P1: The SWR fetch doesn't include the EMAIL_ACCOUNT_HEADER that is used in the POST request. If the API requires this header for account-specific data, the GET will return incorrect results. Consider using a custom fetcher or including the header.

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

<comment>The SWR fetch doesn&#39;t include the `EMAIL_ACCOUNT_HEADER` that is used in the POST request. If the API requires this header for account-specific data, the GET will return incorrect results. Consider using a custom fetcher or including the header.</comment>

<file context>
@@ -0,0 +1,257 @@
+
+export function ExpirationSection() {
+  const { emailAccountId } = useAccount();
+  const { data, isLoading, mutate } = useSWR&lt;ExpirationSettingsResponse&gt;(
+    &quot;/api/user/expiration-settings&quot;,
+  );
</file context>
Fix with Cubic

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.

3 participants

Comments