Skip to content

Comments

feat: add comprehensive E2E flow tests for email processing#1222

Merged
elie222 merged 7 commits intomainfrom
feat/e2e-flow-tests
Jan 9, 2026
Merged

feat: add comprehensive E2E flow tests for email processing#1222
elie222 merged 7 commits intomainfrom
feat/e2e-flow-tests

Conversation

@elie222
Copy link
Owner

@elie222 elie222 commented Jan 7, 2026

User description

Summary

  • Add end-to-end tests that verify complete email processing flows with real Gmail and Outlook accounts
  • Create GitHub Actions workflow with ngrok tunnel for webhook delivery
  • Support flexible AI providers (OpenAI, Anthropic, Google, OpenRouter)

Test Scenarios

  • Full Reply Cycle: Gmail → Outlook → Rule Processing → Draft → Send → Reply
  • Auto-Labeling: Email classification and label application
  • Outbound Tracking: Sent message handling and reply tracking
  • Draft Cleanup: AI draft deletion when user sends manual reply
  • Similarity Score: Verify AI draft vs user-sent content matching

Test plan

  • Set up test accounts (Gmail + Outlook) with OAuth tokens in test database
  • Configure GitHub secrets (E2E_GMAIL_EMAIL, E2E_OUTLOOK_EMAIL, E2E_NGROK_AUTH_TOKEN)
  • Enable workflow via E2E_FLOWS_ENABLED=true repository variable
  • Run pnpm test-e2e:flows locally or trigger workflow dispatch

🤖 Generated with Claude Code


Generated description

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

graph LR
main_("main"):::modified
runSetup_("runSetup"):::modified
FILE_SYSTEM_("FILE_SYSTEM"):::modified
DOCKER_COMPOSE_("DOCKER_COMPOSE"):::modified
main_ -- "Adds --name flag to pass config name to runSetup." --> runSetup_
runSetup_ -- "Writes config to .env.<name> when name provided." --> FILE_SYSTEM_
runSetup_ -- "Compose path unchanged; env filename now depends on name." --> DOCKER_COMPOSE_
classDef added stroke:#15AA7A
classDef removed stroke:#CD5270
classDef modified stroke:#EDAC4C
linkStyle default stroke:#CBD5E1,font-size:13px
Loading

Introduces a robust suite of end-to-end (E2E) flow tests to validate the complete email processing lifecycle, including AI-driven actions like auto-labeling and draft generation, using real Gmail and Outlook accounts. Establishes a dedicated GitHub Actions workflow and local development scripts to facilitate the execution and debugging of these new tests, ensuring the reliability of core application flows.

TopicDetails
Other Other files
Modified files (1)
  • package.json
Latest Contributors(2)
UserCommitDate
rsnodgrass@gmail.comfeat-add-local-Docker-...January 08, 2026
elie222Update-nextjsDecember 30, 2025
Test Environment & CI/CD Setup Configures the necessary infrastructure and tooling to enable the execution of E2E flow tests in both local development and continuous integration environments. This includes a new GitHub Actions workflow that uses ngrok for secure webhook delivery, updates to package.json for test scripts, modifications to vitest.config.mts for environment handling, and enhancements to the CLI setup command for managing environment configurations. It also adds documentation for setting up and running these tests.
Modified files (9)
  • .github/workflows/e2e-flows.yml
  • apps/web/__tests__/e2e/flows/README.md
  • apps/web/__tests__/e2e/flows/config.ts
  • apps/web/__tests__/e2e/flows/setup.ts
  • apps/web/__tests__/e2e/flows/teardown.ts
  • apps/web/package.json
  • apps/web/vitest.config.mts
  • packages/cli/src/main.ts
  • pnpm-lock.yaml
Latest Contributors(0)
UserCommitDate
E2E Flow Test Scenarios Implements comprehensive end-to-end tests that simulate real-world email interactions, verifying critical application flows such as email classification, AI draft creation and cleanup, outbound message tracking, and the full reply cycle across Gmail and Outlook accounts. These tests utilize dedicated helper functions for account management, email operations, logging, polling for state changes, and webhook interactions.
Modified files (9)
  • apps/web/__tests__/e2e/flows/auto-labeling.test.ts
  • apps/web/__tests__/e2e/flows/draft-cleanup.test.ts
  • apps/web/__tests__/e2e/flows/full-reply-cycle.test.ts
  • apps/web/__tests__/e2e/flows/helpers/accounts.ts
  • apps/web/__tests__/e2e/flows/helpers/email.ts
  • apps/web/__tests__/e2e/flows/helpers/logging.ts
  • apps/web/__tests__/e2e/flows/helpers/polling.ts
  • apps/web/__tests__/e2e/flows/helpers/webhook.ts
  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
Latest Contributors(0)
UserCommitDate
This pull request is reviewed by Baz. Review like a pro on (Baz).

Summary by CodeRabbit

  • Tests

    • Added comprehensive end-to-end flow tests for email automation (full reply cycle, auto-labeling, draft cleanup, outbound tracking) plus helpers for accounts, email actions, polling, webhooks, logging, setup, and teardown.
  • Documentation

    • Added detailed E2E testing guide with scenarios, setup, commands, and troubleshooting.
  • Chores

    • Added scheduled/manual E2E CI workflow and local E2E scripts; updated test runner env handling; added a CLI setup flag for custom config naming.

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

Add end-to-end tests that verify complete email processing flows with real
Gmail and Outlook accounts, webhooks, and AI processing.

Test scenarios:
- Full reply cycle: Gmail → Outlook → Rule Processing → Draft → Send → Reply
- Auto-labeling: Email classification and label application
- Outbound tracking: Sent message handling and reply tracking
- Draft cleanup: AI draft deletion when user sends manual reply
- Similarity score verification for AI drafts

Infrastructure:
- GitHub Actions workflow with ngrok tunnel for webhook delivery
- Flexible AI provider support (OpenAI, Anthropic, Google, OpenRouter)
- Polling utilities for async state verification
- Test account management with automatic premium/rules setup

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@vercel
Copy link

vercel bot commented Jan 7, 2026

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

Project Deployment Review Updated (UTC)
inbox-zero Ready Ready Preview Jan 9, 2026 0:35am

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 7, 2026

📝 Walkthrough

Walkthrough

Adds a scheduled/manual GitHub Actions workflow and a complete end-to-end Vitest test suite with config, helpers, setup/teardown, docs, scripts, and CLI option to run Gmail↔Outlook flows using ngrok and Prisma-backed assertions.

Changes

Cohort / File(s) Summary
GitHub Actions Workflow
\.github/workflows/e2e-flows.yml
New scheduled/manual "E2E Flow Tests" workflow with concurrency guard, feature-gate job, ngrok provisioning, app build/start (secrets), and scoped Vitest execution with failure artifact upload.
E2E Test Docs
apps/web/__tests__/e2e/flows/README.md
New README describing setup, required secrets, test structure, scenarios, debugging, timeouts, and troubleshooting.
Test Config
apps/web/__tests__/e2e/flows/config.ts
Env-driven config exports (E2E account emails, run ID, webhook URL, AI model), TIMEOUTS, and helpers: getTestSubjectPrefix, shouldRunFlowTests, validateConfig.
Setup / Teardown
apps/web/__tests__/e2e/flows/setup.ts, apps/web/__tests__/e2e/flows/teardown.ts
Initialization helpers (initializeFlowTests, setupFlowTest) and cleanup/summary utilities (cleanupFlowTest, teardownFlowTests, generateTestSummary).
Helpers — Accounts
apps/web/__tests__/e2e/flows/helpers/accounts.ts
Load Gmail/Outlook test accounts from Prisma, ensure premium/rules, add caching and clearAccountCache.
Helpers — Email
apps/web/__tests__/e2e/flows/helpers/email.ts
Send/reply helpers, assertions for labels/drafts/threads, TEST_EMAIL_SCENARIOS, and inbox cleanup utilities.
Helpers — Logging
apps/web/__tests__/e2e/flows/helpers/logging.ts
In-memory webhook/api-call logs, logStep/logAssertion/logTestSummary, and clearLogs.
Helpers — Polling
apps/web/__tests__/e2e/flows/helpers/polling.ts
Generic pollUntil and waiters: waitForExecutedRule, waitForDraft, waitForLabel, waitForMessageInInbox, waitForDraftDeleted, waitForDraftSendLog.
Helpers — Webhook
apps/web/__tests__/e2e/flows/helpers/webhook.ts
Webhook subscription lifecycle: setupTestWebhookSubscription, teardownTestWebhookSubscription, verifyWebhookSubscription, ensureWebhookSubscription (DB-persisted).
Test Suites
apps/web/__tests__/e2e/flows/*.test.ts
New Vitest suites: full-reply-cycle.test.ts, auto-labeling.test.ts, draft-cleanup.test.ts, outbound-tracking.test.ts — conditional on RUN_E2E_FLOW_TESTS; cover drafts, labels, thread continuity, outbound tracking, and DraftSendLog assertions.
Vitest Env Switch
apps/web/vitest.config.mts
Adds RUN_E2E_FLOW_TESTS-driven selection of .env.e2e vs .env.test.
Package Script (apps/web)
apps/web/package.json
Adds dev:e2e, test-e2e:flows, and prisma:migrate:e2e scripts; adds dotenv-cli devDependency.
Repo-level Package Script & CLI
package.json, packages/cli/src/main.ts
Adds start:cli script and CLI --name option; runSetup signature changed to accept { name?: string } to support named env files.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant GH as GitHub Actions
  participant Runner as Job Runner
  participant Repo as Repository
  participant App as App Server
  participant Ngrok as ngrok
  participant Tests as Vitest E2E Suite
  participant DB as Prisma/Database
  participant Email as Email Providers

  Note over GH,Runner: Scheduled or manual trigger
  GH->>Repo: checkout branch
  Runner->>Runner: setup Node/pnpm, install deps
  Runner->>App: build & start server (env secrets)
  Runner->>Ngrok: start tunnel
  Ngrok-->>Runner: NGROK_URL
  Runner->>Tests: run Vitest with NGROK_URL + secrets
  Tests->>Email: send test emails/replies
  Email->>App: webhook callbacks (via ngrok)
  App->>DB: persist executed rules, drafts, logs
  Tests->>DB: poll & assert via Prisma
  alt failure
    Runner->>GH: upload logs & artifacts
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested reviewers

  • baz-reviewer
  • anakarentorosserrano-star

Poem

🐇 I tunneled through ngrok to chase a testy trail,

Webhooks chimed like wind chimes and logs told quite the tale.
Drafts sprouted like carrots, labels pranced in rows,
Threads held hands together where every message goes.
Hooray — I hopped, I cleaned, I celebrated with a nibble.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: adding comprehensive E2E flow tests for email processing, which aligns with the majority of the changeset focused on E2E test infrastructure and test suites.
Docstring Coverage ✅ Passed Docstring coverage is 97.62% which is sufficient. The required threshold is 80.00%.

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


📜 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 84c1dc2 and 0d3ba7d.

📒 Files selected for processing (1)
  • package.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • package.json
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: test
  • GitHub Check: Analyze (javascript-typescript)

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

❤️ Share

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

Copy link
Contributor

@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.

5 issues found across 15 files

Prompt for AI agents (all issues)

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


<file name="apps/web/__tests__/e2e/flows/auto-labeling.test.ts">

<violation number="1" location="apps/web/__tests__/e2e/flows/auto-labeling.test.ts:162">
P1: Test name says 'without creating draft' but there's no assertion verifying that behavior. Add an assertion like `expect(draftAction).toBeUndefined()` or `expect(draftAction?.draftId).toBeFalsy()` to actually validate the expected outcome.</violation>
</file>

<file name="apps/web/__tests__/e2e/flows/draft-cleanup.test.ts">

<violation number="1" location="apps/web/__tests__/e2e/flows/draft-cleanup.test.ts:276">
P2: Test silently returns without failing if no draft is created. This could mask real issues where the AI should create a draft but doesn't. Consider using `expect.fail()` or `test.skip()` with a clear message to indicate the test precondition wasn't met.</violation>
</file>

<file name="apps/web/__tests__/e2e/flows/outbound-tracking.test.ts">

<violation number="1" location="apps/web/__tests__/e2e/flows/outbound-tracking.test.ts:168">
P1: Test has no assertions and will always pass. The `executedRulesForSent` query result is logged but never verified with `expect()`. Add an assertion such as `expect(executedRulesForSent).toHaveLength(0)` to actually validate the expected behavior.</violation>

<violation number="2" location="apps/web/__tests__/e2e/flows/outbound-tracking.test.ts:231">
P1: Test has no assertions and will always pass. The `threadTracker` query result is only logged, not verified with `expect()`. Add assertions to validate reply tracking behavior, e.g., `expect(threadTracker).toBeDefined()` and `expect(threadTracker?.resolved).toBe(true)`.</violation>
</file>

<file name="apps/web/__tests__/e2e/flows/full-reply-cycle.test.ts">

<violation number="1" location="apps/web/__tests__/e2e/flows/full-reply-cycle.test.ts:155">
P2: The assertion only verifies `labelIds` is defined, but doesn't verify the specific label was applied. This could lead to false positive test results when labeling fails. Consider asserting the expected label is present.</violation>
</file>

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🤖 Fix all issues with AI agents
In @.github/workflows/e2e-flows.yml:
- Around line 195-219: The notify-disabled GitHub Actions job (notify-disabled)
lacks an explicit permissions block; add a minimal permissions stanza to that
job to explicitly grant only the needed permissions (e.g., read for contents,
id-token, and any other specific scopes used by steps) by inserting a
permissions: mapping under the notify-disabled job definition so the workflow
follows least-privilege guidelines and satisfies CodeQL/static analysis checks.
- Around line 49-194: The e2e-flows job lacks an explicit permissions block; add
a permissions stanza under the e2e-flows job that grants only the minimal scopes
needed (for example: contents: read and actions: write) so checkout and artifact
upload work with least privilege, e.g., add permissions: { contents: read,
actions: write } directly beneath the e2e-flows job declaration.
- Around line 32-48: The workflow job "check-enabled" lacks an explicit
permissions block; add a minimal permissions entry (e.g., permissions:
variables: read) to the job to follow least-privilege practices. Update the
"check-enabled" job (the job block named check-enabled that contains the step
with id "check") to include a permissions section that grants only the required
permission (variables: read) so the step can access repository variables without
broader rights.
- Around line 108-114: The health-check loop that polls
http://localhost:3000/api/health (the for i in {1..30} loop) can finish without
the job failing if the server never becomes ready; modify the loop so that after
the attempts it explicitly fails the step when the check never succeeds—for
example, after the loop add a check that if the last curl failed then echo a
clear error like "App server failed to start" and exit 1 (or use the GitHub
Actions failure mechanism) so the workflow stops with a non-zero status.

In @apps/web/__tests__/e2e/flows/draft-cleanup.test.ts:
- Around line 87-88: Replace the non-null assertions by explicitly guarding and
extracting the value: after the truthy check on draftAction?.draftId, add an
explicit runtime guard (e.g., if (!draftAction?.draftId) throw new
Error("missing draftId")) and then assign const aiDraftId = draftAction.draftId;
this removes the use of the `!` operator while preserving the test intent and
locating the change around the draftAction and aiDraftId variables.

In @apps/web/__tests__/e2e/flows/outbound-tracking.test.ts:
- Around line 224-238: The test currently only logs the result of
prisma.threadTracker.findFirst (threadTracker) without verifying expected
behavior; replace the conditional logging with explicit Jest assertions: assert
whether threadTracker should exist (use
expect(threadTracker).toBeDefined()/not.toBeNull() or toBeNull() as appropriate
for the scenario) and assert the expected properties (e.g.,
expect(threadTracker.resolved).toBe(<expectedBoolean>) and
expect(threadTracker.type).toBe(<expectedType>)). Use the existing symbols
threadTracker, prisma.threadTracker.findFirst, receivedMessage.threadId and
outlook.id to locate and assert the correct tracker state after the reply
instead of only logging via logStep.
- Around line 159-175: The test currently fetches executedRulesForSent via
prisma.executedRule.findMany but only logs the length; add a concrete assertion
(e.g., expect(executedRulesForSent.length).toBe(0) or toEqual(expectedCount))
immediately after the query to enforce the desired behavior for outbound
messages; update the assertion to reflect the intended contract (no ExecutedRule
entries for outbound messages) and keep or remove the logStep call as needed.

In @apps/web/__tests__/e2e/flows/README.md:
- Around line 72-87: Add missing language specifiers to the two fenced code
blocks in apps/web/__tests__/e2e/flows/README.md: update the block that shows
the directory tree starting with "flows/" (the block containing "config.ts",
"setup.ts", etc.) and the block that begins with the log line "[E2E-abc123] Step
1: Sending email from Gmail to Outlook" to use a language tag (e.g., change the
opening triple backticks to ```text) so markdownlint/syntax highlighting works
correctly; keep the content unchanged and only modify the opening fence for both
blocks.
🧹 Nitpick comments (6)
.github/workflows/e2e-flows.yml (1)

102-136: Refactor duplicated environment variables.

The same environment variables are declared in both the "Start app server" and "Run E2E Flow Tests" steps. Consider using job-level env to reduce duplication and potential inconsistencies.

🔎 Proposed refactor
   e2e-flows:
     needs: check-enabled
     if: needs.check-enabled.outputs.enabled == 'true'
     runs-on: ubuntu-latest
     timeout-minutes: 30
+    env:
+      DATABASE_URL: ${{ secrets.DATABASE_URL }}
+      UPSTASH_REDIS_REST_URL: ${{ secrets.UPSTASH_REDIS_REST_URL }}
+      UPSTASH_REDIS_REST_TOKEN: ${{ secrets.UPSTASH_REDIS_REST_TOKEN }}
+      GOOGLE_CLIENT_ID: ${{ secrets.GOOGLE_CLIENT_ID }}
+      GOOGLE_CLIENT_SECRET: ${{ secrets.GOOGLE_CLIENT_SECRET }}
+      # ... (move all common env vars here)

     steps:
       # ... other steps ...
       
       - name: Start app server
         run: |
           cd apps/web
           pnpm start &
           # ... health check logic ...
-        env:
-          DATABASE_URL: ${{ secrets.DATABASE_URL }}
-          # ... (remove duplicated env vars)
apps/web/__tests__/e2e/flows/setup.ts (1)

32-44: Consider error handling in the after() mock.

The mocked after() function doesn't handle potential promise rejections from the async function. In production, errors in after() callbacks might be logged separately, but in tests they should fail the test.

🔎 Proposed enhancement
     after: async (fn: () => void | Promise<void>) => {
       // Run the async function and wait for it
-      await fn();
+      try {
+        await fn();
+      } catch (error) {
+        console.error("Error in after() callback:", error);
+        throw error;
+      }
     },
apps/web/__tests__/e2e/flows/auto-labeling.test.ts (2)

34-37: Consider cleaning up test artifacts before generating summary.

The afterEach hook generates a summary immediately after each test, but doesn't clean up created emails, drafts, or executed rules. If these artifacts affect subsequent tests or dashboard views, consider adding cleanup logic before the summary.

Example cleanup pattern:

afterEach(async () => {
  // Optional: Clean up test artifacts
  // await cleanupTestEmails(gmail, outlook);
  // await cleanupTestDrafts(outlook);
  
  generateTestSummary("Auto-Labeling", testStartTime);
  clearLogs();
});

95-108: Consider verifying specific label names.

The test verifies that processing occurred and actions were taken, but doesn't assert on specific label names. If label classification is a critical feature, consider adding assertions for expected label values.

Example enhancement:

// Verify specific labels were applied
const expectedLabels = ["Needs Reply", "Important"]; // adjust based on rules
const hasExpectedLabels = expectedLabels.some(label => 
  message.labelIds?.includes(label)
);
expect(hasExpectedLabels).toBe(true);

This would make the test more explicit about what "correct classification" means.

apps/web/__tests__/e2e/flows/helpers/accounts.ts (1)

26-27: Consider documenting cache lifecycle expectations.

Module-level caching of test accounts can improve performance but may cause test interdependence if not managed carefully. Consider documenting when clearAccountCache() should be called (e.g., in test teardown hooks) to ensure proper test isolation.

Also applies to: 236-239

apps/web/__tests__/e2e/flows/helpers/email.ts (1)

174-198: Consider simplifying draft deletion verification logic.

The error handling logic works correctly but could be clearer. The function handles three cases: draft still exists (throw), draft not found via API error (success), and draft returns null (success). Consider restructuring to make the flow more explicit, or add a comment explaining the error discrimination pattern.

Possible simplification
 export async function assertDraftDeleted(options: {
   provider: EmailProvider;
   draftId: string;
 }): Promise<void> {
   const { provider, draftId } = options;
 
   logStep("Checking draft deleted", { draftId });
 
   try {
     const draft = await provider.getDraft(draftId);
     if (draft) {
       throw new Error(`Expected draft ${draftId} to be deleted, but it exists`);
     }
+    // Draft returned null - successfully deleted
+    logAssertion("Draft deleted", true);
   } catch (error) {
-    // Draft not found is expected
-    if (error instanceof Error && !error.message.includes("to be deleted")) {
-      // API error means draft doesn't exist - good
-      logAssertion("Draft deleted", true);
-      return;
+    if (error instanceof Error && error.message.includes("to be deleted")) {
+      // This is our own error from above - draft still exists
+      throw error;
     }
-    throw error;
+    // API error (e.g., 404) means draft doesn't exist - this is expected
+    logAssertion("Draft deleted", true);
   }
-
-  logAssertion("Draft deleted", true);
 }
📜 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 68d8c11 and 15adcc4.

📒 Files selected for processing (15)
  • .github/workflows/e2e-flows.yml
  • apps/web/__tests__/e2e/flows/README.md
  • apps/web/__tests__/e2e/flows/auto-labeling.test.ts
  • apps/web/__tests__/e2e/flows/config.ts
  • apps/web/__tests__/e2e/flows/draft-cleanup.test.ts
  • apps/web/__tests__/e2e/flows/full-reply-cycle.test.ts
  • apps/web/__tests__/e2e/flows/helpers/accounts.ts
  • apps/web/__tests__/e2e/flows/helpers/email.ts
  • apps/web/__tests__/e2e/flows/helpers/logging.ts
  • apps/web/__tests__/e2e/flows/helpers/polling.ts
  • apps/web/__tests__/e2e/flows/helpers/webhook.ts
  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
  • apps/web/__tests__/e2e/flows/setup.ts
  • apps/web/__tests__/e2e/flows/teardown.ts
  • apps/web/package.json
🧰 Additional context used
📓 Path-based instructions (23)
**/*.{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/__tests__/e2e/flows/full-reply-cycle.test.ts
  • apps/web/__tests__/e2e/flows/helpers/webhook.ts
  • apps/web/__tests__/e2e/flows/auto-labeling.test.ts
  • apps/web/__tests__/e2e/flows/draft-cleanup.test.ts
  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
  • apps/web/__tests__/e2e/flows/teardown.ts
  • apps/web/__tests__/e2e/flows/helpers/accounts.ts
  • apps/web/__tests__/e2e/flows/config.ts
  • apps/web/__tests__/e2e/flows/setup.ts
  • apps/web/__tests__/e2e/flows/helpers/logging.ts
  • apps/web/__tests__/e2e/flows/helpers/polling.ts
  • apps/web/__tests__/e2e/flows/helpers/email.ts
apps/web/__tests__/**/*.test.ts

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

apps/web/__tests__/**/*.test.ts: Place all LLM-related tests in apps/web/__tests__/ directory
Use vitest imports (describe, expect, test, vi, beforeEach) in LLM test files
Mock 'server-only' module with empty object in LLM test files: vi.mock("server-only", () => ({}))
Set timeout constant const TIMEOUT = 15_000; for LLM tests
Use describe.runIf(isAiTest) with environment variable RUN_AI_TESTS === "true" to conditionally run LLM tests
Use console.debug() for outputting generated LLM content in tests, e.g., console.debug("Generated content:\n", result.content);
Prefer using existing helpers from @/__tests__/helpers.ts (getEmailAccount, getEmail, getRule, getMockMessage, getMockExecutedRule) instead of creating custom test data helpers

Files:

  • apps/web/__tests__/e2e/flows/full-reply-cycle.test.ts
  • apps/web/__tests__/e2e/flows/auto-labeling.test.ts
  • apps/web/__tests__/e2e/flows/draft-cleanup.test.ts
  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
apps/web/{utils/ai,utils/llms,__tests__}/**/*.ts

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

LLM-related code must be organized in specific directories: apps/web/utils/ai/ for main implementations, apps/web/utils/llms/ for core utilities and configurations, and apps/web/__tests__/ for LLM-specific tests

Files:

  • apps/web/__tests__/e2e/flows/full-reply-cycle.test.ts
  • apps/web/__tests__/e2e/flows/helpers/webhook.ts
  • apps/web/__tests__/e2e/flows/auto-labeling.test.ts
  • apps/web/__tests__/e2e/flows/draft-cleanup.test.ts
  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
  • apps/web/__tests__/e2e/flows/teardown.ts
  • apps/web/__tests__/e2e/flows/helpers/accounts.ts
  • apps/web/__tests__/e2e/flows/config.ts
  • apps/web/__tests__/e2e/flows/setup.ts
  • apps/web/__tests__/e2e/flows/helpers/logging.ts
  • apps/web/__tests__/e2e/flows/helpers/polling.ts
  • apps/web/__tests__/e2e/flows/helpers/email.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/__tests__/e2e/flows/full-reply-cycle.test.ts
  • apps/web/__tests__/e2e/flows/helpers/webhook.ts
  • apps/web/__tests__/e2e/flows/auto-labeling.test.ts
  • apps/web/__tests__/e2e/flows/draft-cleanup.test.ts
  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
  • apps/web/__tests__/e2e/flows/teardown.ts
  • apps/web/__tests__/e2e/flows/helpers/accounts.ts
  • apps/web/__tests__/e2e/flows/config.ts
  • apps/web/__tests__/e2e/flows/setup.ts
  • apps/web/__tests__/e2e/flows/helpers/logging.ts
  • apps/web/__tests__/e2e/flows/helpers/polling.ts
  • apps/web/__tests__/e2e/flows/helpers/email.ts
apps/web/**/*.{ts,tsx}

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

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

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

Files:

  • apps/web/__tests__/e2e/flows/full-reply-cycle.test.ts
  • apps/web/__tests__/e2e/flows/helpers/webhook.ts
  • apps/web/__tests__/e2e/flows/auto-labeling.test.ts
  • apps/web/__tests__/e2e/flows/draft-cleanup.test.ts
  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
  • apps/web/__tests__/e2e/flows/teardown.ts
  • apps/web/__tests__/e2e/flows/helpers/accounts.ts
  • apps/web/__tests__/e2e/flows/config.ts
  • apps/web/__tests__/e2e/flows/setup.ts
  • apps/web/__tests__/e2e/flows/helpers/logging.ts
  • apps/web/__tests__/e2e/flows/helpers/polling.ts
  • apps/web/__tests__/e2e/flows/helpers/email.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/__tests__/e2e/flows/full-reply-cycle.test.ts
  • apps/web/__tests__/e2e/flows/helpers/webhook.ts
  • apps/web/__tests__/e2e/flows/auto-labeling.test.ts
  • apps/web/__tests__/e2e/flows/draft-cleanup.test.ts
  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
  • apps/web/__tests__/e2e/flows/teardown.ts
  • apps/web/__tests__/e2e/flows/helpers/accounts.ts
  • apps/web/__tests__/e2e/flows/config.ts
  • apps/web/__tests__/e2e/flows/setup.ts
  • apps/web/__tests__/e2e/flows/helpers/logging.ts
  • apps/web/__tests__/e2e/flows/helpers/polling.ts
  • apps/web/__tests__/e2e/flows/helpers/email.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/__tests__/e2e/flows/full-reply-cycle.test.ts
  • apps/web/__tests__/e2e/flows/helpers/webhook.ts
  • apps/web/__tests__/e2e/flows/auto-labeling.test.ts
  • apps/web/__tests__/e2e/flows/draft-cleanup.test.ts
  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
  • apps/web/__tests__/e2e/flows/teardown.ts
  • apps/web/__tests__/e2e/flows/helpers/accounts.ts
  • apps/web/__tests__/e2e/flows/config.ts
  • apps/web/__tests__/e2e/flows/setup.ts
  • apps/web/__tests__/e2e/flows/helpers/logging.ts
  • apps/web/__tests__/e2e/flows/helpers/polling.ts
  • apps/web/__tests__/e2e/flows/helpers/email.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/__tests__/e2e/flows/full-reply-cycle.test.ts
  • apps/web/__tests__/e2e/flows/helpers/webhook.ts
  • apps/web/__tests__/e2e/flows/auto-labeling.test.ts
  • apps/web/__tests__/e2e/flows/draft-cleanup.test.ts
  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
  • apps/web/__tests__/e2e/flows/teardown.ts
  • apps/web/__tests__/e2e/flows/helpers/accounts.ts
  • apps/web/__tests__/e2e/flows/config.ts
  • apps/web/__tests__/e2e/flows/setup.ts
  • apps/web/__tests__/e2e/flows/helpers/logging.ts
  • apps/web/__tests__/e2e/flows/helpers/polling.ts
  • apps/web/__tests__/e2e/flows/helpers/email.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/__tests__/e2e/flows/full-reply-cycle.test.ts
  • apps/web/__tests__/e2e/flows/helpers/webhook.ts
  • apps/web/__tests__/e2e/flows/auto-labeling.test.ts
  • apps/web/__tests__/e2e/flows/draft-cleanup.test.ts
  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
  • apps/web/__tests__/e2e/flows/teardown.ts
  • apps/web/__tests__/e2e/flows/helpers/accounts.ts
  • apps/web/__tests__/e2e/flows/config.ts
  • apps/web/__tests__/e2e/flows/setup.ts
  • apps/web/__tests__/e2e/flows/helpers/logging.ts
  • apps/web/__tests__/e2e/flows/helpers/polling.ts
  • apps/web/__tests__/e2e/flows/helpers/email.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/__tests__/e2e/flows/full-reply-cycle.test.ts
  • apps/web/__tests__/e2e/flows/auto-labeling.test.ts
  • apps/web/__tests__/e2e/flows/draft-cleanup.test.ts
  • apps/web/__tests__/e2e/flows/outbound-tracking.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/__tests__/e2e/flows/full-reply-cycle.test.ts
  • apps/web/__tests__/e2e/flows/helpers/webhook.ts
  • apps/web/__tests__/e2e/flows/auto-labeling.test.ts
  • apps/web/__tests__/e2e/flows/README.md
  • apps/web/__tests__/e2e/flows/draft-cleanup.test.ts
  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
  • apps/web/__tests__/e2e/flows/teardown.ts
  • .github/workflows/e2e-flows.yml
  • apps/web/__tests__/e2e/flows/helpers/accounts.ts
  • apps/web/__tests__/e2e/flows/config.ts
  • apps/web/__tests__/e2e/flows/setup.ts
  • apps/web/package.json
  • apps/web/__tests__/e2e/flows/helpers/logging.ts
  • apps/web/__tests__/e2e/flows/helpers/polling.ts
  • apps/web/__tests__/e2e/flows/helpers/email.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/__tests__/e2e/flows/full-reply-cycle.test.ts
  • apps/web/__tests__/e2e/flows/helpers/webhook.ts
  • apps/web/__tests__/e2e/flows/auto-labeling.test.ts
  • apps/web/__tests__/e2e/flows/draft-cleanup.test.ts
  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
  • apps/web/__tests__/e2e/flows/teardown.ts
  • apps/web/__tests__/e2e/flows/helpers/accounts.ts
  • apps/web/__tests__/e2e/flows/config.ts
  • apps/web/__tests__/e2e/flows/setup.ts
  • apps/web/__tests__/e2e/flows/helpers/logging.ts
  • apps/web/__tests__/e2e/flows/helpers/polling.ts
  • apps/web/__tests__/e2e/flows/helpers/email.ts
**/{scripts,tests,__tests__}/**/*.{ts,tsx}

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

Use createScopedLogger only for code that doesn't run within a middleware chain, such as standalone scripts or tests

Files:

  • apps/web/__tests__/e2e/flows/full-reply-cycle.test.ts
  • apps/web/__tests__/e2e/flows/helpers/webhook.ts
  • apps/web/__tests__/e2e/flows/auto-labeling.test.ts
  • apps/web/__tests__/e2e/flows/draft-cleanup.test.ts
  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
  • apps/web/__tests__/e2e/flows/teardown.ts
  • apps/web/__tests__/e2e/flows/helpers/accounts.ts
  • apps/web/__tests__/e2e/flows/config.ts
  • apps/web/__tests__/e2e/flows/setup.ts
  • apps/web/__tests__/e2e/flows/helpers/logging.ts
  • apps/web/__tests__/e2e/flows/helpers/polling.ts
  • apps/web/__tests__/e2e/flows/helpers/email.ts
apps/web/**/*.{ts,tsx,js,jsx}

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

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

Files:

  • apps/web/__tests__/e2e/flows/full-reply-cycle.test.ts
  • apps/web/__tests__/e2e/flows/helpers/webhook.ts
  • apps/web/__tests__/e2e/flows/auto-labeling.test.ts
  • apps/web/__tests__/e2e/flows/draft-cleanup.test.ts
  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
  • apps/web/__tests__/e2e/flows/teardown.ts
  • apps/web/__tests__/e2e/flows/helpers/accounts.ts
  • apps/web/__tests__/e2e/flows/config.ts
  • apps/web/__tests__/e2e/flows/setup.ts
  • apps/web/__tests__/e2e/flows/helpers/logging.ts
  • apps/web/__tests__/e2e/flows/helpers/polling.ts
  • apps/web/__tests__/e2e/flows/helpers/email.ts
apps/web/**/*.{ts,tsx,js,jsx,json,css}

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

Format code with Prettier

Files:

  • apps/web/__tests__/e2e/flows/full-reply-cycle.test.ts
  • apps/web/__tests__/e2e/flows/helpers/webhook.ts
  • apps/web/__tests__/e2e/flows/auto-labeling.test.ts
  • apps/web/__tests__/e2e/flows/draft-cleanup.test.ts
  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
  • apps/web/__tests__/e2e/flows/teardown.ts
  • apps/web/__tests__/e2e/flows/helpers/accounts.ts
  • apps/web/__tests__/e2e/flows/config.ts
  • apps/web/__tests__/e2e/flows/setup.ts
  • apps/web/package.json
  • apps/web/__tests__/e2e/flows/helpers/logging.ts
  • apps/web/__tests__/e2e/flows/helpers/polling.ts
  • apps/web/__tests__/e2e/flows/helpers/email.ts
apps/web/**/*.test.{ts,tsx}

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

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

Files:

  • apps/web/__tests__/e2e/flows/full-reply-cycle.test.ts
  • apps/web/__tests__/e2e/flows/auto-labeling.test.ts
  • apps/web/__tests__/e2e/flows/draft-cleanup.test.ts
  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
apps/web/**/*.{example,ts,json}

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

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

Files:

  • apps/web/__tests__/e2e/flows/full-reply-cycle.test.ts
  • apps/web/__tests__/e2e/flows/helpers/webhook.ts
  • apps/web/__tests__/e2e/flows/auto-labeling.test.ts
  • apps/web/__tests__/e2e/flows/draft-cleanup.test.ts
  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
  • apps/web/__tests__/e2e/flows/teardown.ts
  • apps/web/__tests__/e2e/flows/helpers/accounts.ts
  • apps/web/__tests__/e2e/flows/config.ts
  • apps/web/__tests__/e2e/flows/setup.ts
  • apps/web/package.json
  • apps/web/__tests__/e2e/flows/helpers/logging.ts
  • apps/web/__tests__/e2e/flows/helpers/polling.ts
  • apps/web/__tests__/e2e/flows/helpers/email.ts
**/*.test.{ts,tsx}

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

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

Files:

  • apps/web/__tests__/e2e/flows/full-reply-cycle.test.ts
  • apps/web/__tests__/e2e/flows/auto-labeling.test.ts
  • apps/web/__tests__/e2e/flows/draft-cleanup.test.ts
  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
**/__tests__/**/*.{ts,tsx}

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

Place AI tests in the __tests__ directory and do not run them by default as they use a real LLM

Files:

  • apps/web/__tests__/e2e/flows/full-reply-cycle.test.ts
  • apps/web/__tests__/e2e/flows/helpers/webhook.ts
  • apps/web/__tests__/e2e/flows/auto-labeling.test.ts
  • apps/web/__tests__/e2e/flows/draft-cleanup.test.ts
  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
  • apps/web/__tests__/e2e/flows/teardown.ts
  • apps/web/__tests__/e2e/flows/helpers/accounts.ts
  • apps/web/__tests__/e2e/flows/config.ts
  • apps/web/__tests__/e2e/flows/setup.ts
  • apps/web/__tests__/e2e/flows/helpers/logging.ts
  • apps/web/__tests__/e2e/flows/helpers/polling.ts
  • apps/web/__tests__/e2e/flows/helpers/email.ts
**/*.test.{js,jsx,ts,tsx}

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

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

Files:

  • apps/web/__tests__/e2e/flows/full-reply-cycle.test.ts
  • apps/web/__tests__/e2e/flows/auto-labeling.test.ts
  • apps/web/__tests__/e2e/flows/draft-cleanup.test.ts
  • apps/web/__tests__/e2e/flows/outbound-tracking.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/__tests__/e2e/flows/helpers/webhook.ts
  • apps/web/__tests__/e2e/flows/helpers/accounts.ts
  • apps/web/__tests__/e2e/flows/helpers/logging.ts
  • apps/web/__tests__/e2e/flows/helpers/polling.ts
  • apps/web/__tests__/e2e/flows/helpers/email.ts
**/package.json

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

Use pnpm as the package manager

Files:

  • apps/web/package.json
apps/web/package.json

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

Don't install packages in root; install in apps/web workspace instead

Files:

  • apps/web/package.json
🧠 Learnings (40)
📓 Common learnings
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.775Z
Learning: Applies to **/*.test.{ts,tsx} : Use test helper functions `getEmail`, `getEmailAccount`, and `getRule` from `@/__tests__/helpers` for creating mock data
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
📚 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/__tests__/e2e/flows/full-reply-cycle.test.ts
  • apps/web/__tests__/e2e/flows/helpers/webhook.ts
  • apps/web/__tests__/e2e/flows/auto-labeling.test.ts
  • apps/web/__tests__/e2e/flows/draft-cleanup.test.ts
  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
  • apps/web/__tests__/e2e/flows/teardown.ts
  • apps/web/__tests__/e2e/flows/helpers/accounts.ts
  • apps/web/__tests__/e2e/flows/config.ts
  • apps/web/__tests__/e2e/flows/setup.ts
  • apps/web/__tests__/e2e/flows/helpers/logging.ts
  • apps/web/__tests__/e2e/flows/helpers/polling.ts
  • apps/web/__tests__/e2e/flows/helpers/email.ts
📚 Learning: 2026-01-01T10:42:29.775Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.775Z
Learning: Applies to **/*.test.{ts,tsx} : Use test helper functions `getEmail`, `getEmailAccount`, and `getRule` from `@/__tests__/helpers` for creating mock data

Applied to files:

  • apps/web/__tests__/e2e/flows/full-reply-cycle.test.ts
  • apps/web/__tests__/e2e/flows/helpers/webhook.ts
  • apps/web/__tests__/e2e/flows/auto-labeling.test.ts
  • apps/web/__tests__/e2e/flows/README.md
  • apps/web/__tests__/e2e/flows/draft-cleanup.test.ts
  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
  • apps/web/__tests__/e2e/flows/teardown.ts
  • apps/web/__tests__/e2e/flows/helpers/accounts.ts
  • apps/web/__tests__/e2e/flows/config.ts
  • apps/web/__tests__/e2e/flows/setup.ts
  • apps/web/__tests__/e2e/flows/helpers/logging.ts
  • apps/web/__tests__/e2e/flows/helpers/polling.ts
  • apps/web/__tests__/e2e/flows/helpers/email.ts
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/**/*.test.{ts,tsx} : Co-locate test files next to source files (e.g., `utils/example.test.ts`). Only E2E and AI tests go in `__tests__/`

Applied to files:

  • apps/web/__tests__/e2e/flows/full-reply-cycle.test.ts
  • apps/web/__tests__/e2e/flows/helpers/webhook.ts
  • apps/web/__tests__/e2e/flows/auto-labeling.test.ts
  • apps/web/__tests__/e2e/flows/draft-cleanup.test.ts
  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
  • apps/web/__tests__/e2e/flows/teardown.ts
  • apps/web/__tests__/e2e/flows/helpers/accounts.ts
  • apps/web/__tests__/e2e/flows/config.ts
  • apps/web/__tests__/e2e/flows/setup.ts
  • apps/web/package.json
  • apps/web/__tests__/e2e/flows/helpers/logging.ts
  • apps/web/__tests__/e2e/flows/helpers/email.ts
📚 Learning: 2025-11-25T14:37:56.430Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Use vitest imports (`describe`, `expect`, `test`, `vi`, `beforeEach`) in LLM test files

Applied to files:

  • apps/web/__tests__/e2e/flows/full-reply-cycle.test.ts
  • apps/web/__tests__/e2e/flows/auto-labeling.test.ts
  • apps/web/__tests__/e2e/flows/draft-cleanup.test.ts
  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
  • apps/web/__tests__/e2e/flows/teardown.ts
  • apps/web/__tests__/e2e/flows/config.ts
  • apps/web/package.json
  • apps/web/__tests__/e2e/flows/helpers/logging.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/__tests__/e2e/flows/full-reply-cycle.test.ts
  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
  • apps/web/__tests__/e2e/flows/teardown.ts
  • apps/web/__tests__/e2e/flows/helpers/accounts.ts
  • apps/web/__tests__/e2e/flows/helpers/polling.ts
  • apps/web/__tests__/e2e/flows/helpers/email.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/**/*.ts : Place custom hooks in the `apps/web/hooks/` directory

Applied to files:

  • apps/web/__tests__/e2e/flows/helpers/webhook.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/__tests__/e2e/flows/helpers/webhook.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/__tests__/e2e/flows/helpers/webhook.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/__tests__/e2e/flows/helpers/webhook.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/__tests__/e2e/flows/helpers/webhook.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/__tests__/e2e/flows/helpers/webhook.ts
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to **/*.{ts,tsx} : Use wrapper functions for Gmail label operations from @/utils/gmail/label.ts instead of direct API calls

Applied to files:

  • apps/web/__tests__/e2e/flows/auto-labeling.test.ts
  • apps/web/__tests__/e2e/flows/helpers/email.ts
📚 Learning: 2025-11-25T14:37:56.430Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Place all LLM-related tests in `apps/web/__tests__/` directory

Applied to files:

  • apps/web/__tests__/e2e/flows/auto-labeling.test.ts
  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
📚 Learning: 2025-11-25T14:37:56.430Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Use `describe.runIf(isAiTest)` with environment variable `RUN_AI_TESTS === "true"` to conditionally run LLM tests

Applied to files:

  • apps/web/__tests__/e2e/flows/auto-labeling.test.ts
  • apps/web/__tests__/e2e/flows/draft-cleanup.test.ts
  • apps/web/__tests__/e2e/flows/config.ts
  • apps/web/package.json
📚 Learning: 2026-01-01T10:42:29.775Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.775Z
Learning: Applies to **/*.test.{ts,tsx} : Use descriptive test names that clearly indicate what is being tested

Applied to files:

  • apps/web/__tests__/e2e/flows/auto-labeling.test.ts
  • apps/web/__tests__/e2e/flows/teardown.ts
  • apps/web/__tests__/e2e/flows/config.ts
📚 Learning: 2025-11-25T14:37:22.660Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/gmail-api.mdc:0-0
Timestamp: 2025-11-25T14:37:22.660Z
Learning: Applies to apps/web/utils/gmail/**/*.{ts,tsx} : Keep Gmail provider-specific implementation details isolated within the apps/web/utils/gmail/ directory

Applied to files:

  • apps/web/__tests__/e2e/flows/auto-labeling.test.ts
  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
  • apps/web/__tests__/e2e/flows/teardown.ts
  • apps/web/__tests__/e2e/flows/helpers/accounts.ts
  • apps/web/__tests__/e2e/flows/config.ts
  • apps/web/__tests__/e2e/flows/helpers/email.ts
📚 Learning: 2026-01-01T10:42:29.775Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.775Z
Learning: Applies to **/__tests__/**/*.{ts,tsx} : Place AI tests in the `__tests__` directory and do not run them by default as they use a real LLM

Applied to files:

  • apps/web/__tests__/e2e/flows/auto-labeling.test.ts
  • apps/web/__tests__/e2e/flows/draft-cleanup.test.ts
  • apps/web/__tests__/e2e/flows/config.ts
  • apps/web/package.json
📚 Learning: 2026-01-04T00:25:22.311Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/notes.mdc:0-0
Timestamp: 2026-01-04T00:25:22.311Z
Learning: Applies to **/*.test.{js,jsx,ts,tsx} : Co-locate test files next to source files (e.g., `utils/example.test.ts`). Only E2E and AI tests go in `__tests__/`

Applied to files:

  • apps/web/__tests__/e2e/flows/draft-cleanup.test.ts
  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
  • apps/web/__tests__/e2e/flows/teardown.ts
  • apps/web/__tests__/e2e/flows/config.ts
  • apps/web/package.json
📚 Learning: 2025-11-25T14:37:56.430Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Use `console.debug()` for outputting generated LLM content in tests, e.g., `console.debug("Generated content:\n", result.content);`

Applied to files:

  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
  • apps/web/__tests__/e2e/flows/teardown.ts
  • apps/web/__tests__/e2e/flows/helpers/logging.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/__tests__/e2e/flows/teardown.ts
  • apps/web/__tests__/e2e/flows/helpers/accounts.ts
  • apps/web/__tests__/e2e/flows/helpers/email.ts
📚 Learning: 2025-11-25T14:39:27.909Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/security.mdc:0-0
Timestamp: 2025-11-25T14:39:27.909Z
Learning: Applies to **/app/api/**/*.ts : Use `withEmailAccount` middleware for operations scoped to a specific email account, including reading/writing emails, rules, schedules, or any operation using `emailAccountId`

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • apps/web/__tests__/e2e/flows/helpers/accounts.ts
📚 Learning: 2025-11-25T14:37:56.430Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Set timeout constant `const TIMEOUT = 15_000;` for LLM tests

Applied to files:

  • apps/web/__tests__/e2e/flows/config.ts
  • apps/web/__tests__/e2e/flows/helpers/polling.ts
📚 Learning: 2025-11-25T14:37:56.430Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Mock 'server-only' module with empty object in LLM test files: `vi.mock("server-only", () => ({}))`

Applied to files:

  • apps/web/__tests__/e2e/flows/setup.ts
📚 Learning: 2026-01-01T10:42:29.775Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.775Z
Learning: Applies to **/*.test.{ts,tsx} : Clear all mocks between tests using `beforeEach(() => { vi.clearAllMocks(); })`

Applied to files:

  • apps/web/__tests__/e2e/flows/setup.ts
📚 Learning: 2026-01-01T10:42:29.775Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.775Z
Learning: Run tests using `cd apps/web && pnpm test --run` instead of `npx vitest`. Don't use sandbox or the test won't run

Applied to files:

  • apps/web/package.json
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/**/*.{example,ts,json} : Add environment variables to `.env.example`, `env.ts`, and `turbo.json`

Applied to files:

  • apps/web/package.json
📚 Learning: 2026-01-01T10:42:29.775Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.775Z
Learning: Applies to **/*.test.{ts,tsx} : Use `vitest` as the testing framework

Applied to files:

  • apps/web/package.json
📚 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/__tests__/e2e/flows/helpers/logging.ts
📚 Learning: 2025-12-18T16:37:47.972Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/logging.mdc:0-0
Timestamp: 2025-12-18T16:37:47.972Z
Learning: Applies to **/{utils,helpers,lib}/**/*.{ts,tsx} : Logger should be passed as a parameter to helper functions instead of creating their own logger instances

Applied to files:

  • apps/web/__tests__/e2e/flows/helpers/logging.ts
📚 Learning: 2025-12-18T16:37:47.972Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/logging.mdc:0-0
Timestamp: 2025-12-18T16:37:47.972Z
Learning: Applies to **/{scripts,tests,__tests__}/**/*.{ts,tsx} : Use createScopedLogger only for code that doesn't run within a middleware chain, such as standalone scripts or tests

Applied to files:

  • apps/web/__tests__/e2e/flows/helpers/logging.ts
📚 Learning: 2025-12-18T16:37:47.972Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/logging.mdc:0-0
Timestamp: 2025-12-18T16:37:47.972Z
Learning: Applies to **/*.{ts,tsx} : Avoid using .with() for global/file-level loggers; only use .with() within specific functions for context enrichment

Applied to files:

  • apps/web/__tests__/e2e/flows/helpers/logging.ts
📚 Learning: 2026-01-01T10:42:29.775Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.775Z
Learning: Applies to **/*.test.{ts,tsx} : Do not mock the Logger in tests

Applied to files:

  • apps/web/__tests__/e2e/flows/helpers/logging.ts
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{test,spec}.{js,jsx,ts,tsx} : Don't use callbacks in asynchronous tests and hooks

Applied to files:

  • apps/web/__tests__/e2e/flows/helpers/logging.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/__tests__/e2e/flows/helpers/polling.ts
🧬 Code graph analysis (7)
apps/web/__tests__/e2e/flows/helpers/webhook.ts (3)
apps/web/utils/logger.ts (1)
  • createScopedLogger (18-82)
apps/web/__tests__/e2e/flows/helpers/accounts.ts (1)
  • TestAccount (18-24)
apps/web/__tests__/e2e/flows/helpers/logging.ts (1)
  • logStep (100-103)
apps/web/__tests__/e2e/flows/draft-cleanup.test.ts (7)
apps/web/__tests__/e2e/flows/config.ts (3)
  • shouldRunFlowTests (47-52)
  • TIMEOUTS (28-39)
  • getTestSubjectPrefix (42-44)
apps/web/__tests__/e2e/flows/helpers/accounts.ts (1)
  • TestAccount (18-24)
apps/web/__tests__/e2e/flows/setup.ts (2)
  • initializeFlowTests (51-85)
  • setupFlowTest (92-100)
apps/web/__tests__/e2e/flows/teardown.ts (1)
  • generateTestSummary (95-111)
apps/web/__tests__/e2e/flows/helpers/logging.ts (2)
  • clearLogs (92-95)
  • logStep (100-103)
apps/web/__tests__/e2e/flows/helpers/email.ts (3)
  • TEST_EMAIL_SCENARIOS (232-266)
  • sendTestEmail (28-59)
  • sendTestReply (64-106)
apps/web/__tests__/e2e/flows/helpers/polling.ts (4)
  • waitForMessageInInbox (219-252)
  • waitForExecutedRule (62-126)
  • waitForDraftDeleted (257-282)
  • waitForDraftSendLog (290-349)
apps/web/__tests__/e2e/flows/outbound-tracking.test.ts (5)
apps/web/__tests__/e2e/flows/config.ts (3)
  • shouldRunFlowTests (47-52)
  • TIMEOUTS (28-39)
  • getTestSubjectPrefix (42-44)
apps/web/__tests__/e2e/flows/setup.ts (2)
  • initializeFlowTests (51-85)
  • setupFlowTest (92-100)
apps/web/__tests__/e2e/flows/helpers/logging.ts (2)
  • clearLogs (92-95)
  • logStep (100-103)
apps/web/__tests__/e2e/flows/helpers/email.ts (2)
  • sendTestEmail (28-59)
  • sendTestReply (64-106)
apps/web/__tests__/e2e/flows/helpers/polling.ts (1)
  • waitForMessageInInbox (219-252)
apps/web/__tests__/e2e/flows/teardown.ts (4)
apps/web/__tests__/e2e/flows/helpers/logging.ts (5)
  • logStep (100-103)
  • clearLogs (92-95)
  • getWebhookLog (78-80)
  • getApiCallLog (85-87)
  • logTestSummary (121-140)
apps/web/__tests__/e2e/flows/helpers/accounts.ts (3)
  • getGmailTestAccount (32-82)
  • getOutlookTestAccount (87-137)
  • clearAccountCache (236-239)
apps/web/__tests__/e2e/flows/config.ts (1)
  • getTestSubjectPrefix (42-44)
apps/web/__tests__/e2e/flows/helpers/email.ts (1)
  • cleanupTestEmails (271-299)
apps/web/__tests__/e2e/flows/helpers/logging.ts (1)
apps/web/__tests__/e2e/flows/config.ts (1)
  • E2E_RUN_ID (17-19)
apps/web/__tests__/e2e/flows/helpers/polling.ts (3)
apps/web/__tests__/e2e/flows/config.ts (1)
  • TIMEOUTS (28-39)
apps/web/__tests__/e2e/flows/helpers/logging.ts (1)
  • logStep (100-103)
apps/web/utils/email/types.ts (1)
  • EmailProvider (45-251)
apps/web/__tests__/e2e/flows/helpers/email.ts (4)
apps/web/__tests__/e2e/flows/helpers/accounts.ts (1)
  • TestAccount (18-24)
apps/web/__tests__/e2e/flows/config.ts (1)
  • getTestSubjectPrefix (42-44)
apps/web/__tests__/e2e/flows/helpers/logging.ts (2)
  • logStep (100-103)
  • logAssertion (108-116)
apps/web/utils/email/types.ts (1)
  • EmailProvider (45-251)
🪛 GitHub Check: CodeQL
.github/workflows/e2e-flows.yml

[warning] 34-49: Workflow does not contain permissions
Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {{}}


[warning] 50-195: Workflow does not contain permissions
Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {{contents: read}}


[warning] 196-219: Workflow does not contain permissions
Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {{}}

🪛 markdownlint-cli2 (0.18.1)
apps/web/__tests__/e2e/flows/README.md

72-72: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


124-124: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

⏰ 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: Baz Reviewer
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: test
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (3)
apps/web/package.json (1)

15-15: LGTM! E2E flows test script follows established patterns.

The script correctly uses cross-env for cross-platform environment variable handling, scopes to the __tests__/e2e/flows directory, and follows the naming convention of existing test scripts.

apps/web/__tests__/e2e/flows/setup.ts (1)

51-85: LGTM! Well-structured initialization flow.

The initialization function properly validates configuration, loads accounts, ensures prerequisites (premium status, rules), and sets up webhooks. The step-by-step logging will help with debugging test failures.

apps/web/__tests__/e2e/flows/auto-labeling.test.ts (1)

22-293: LGTM! Comprehensive auto-labeling test coverage.

The test suite covers multiple email scenarios (needs reply, FYI, thank you, question) with proper:

  • Conditional execution via shouldRunFlowTests()
  • Timeout configuration
  • Step-by-step logging for debugging
  • Webhook and rule execution verification
  • Test isolation via individual test start times

The tests follow E2E best practices and properly leverage the shared test infrastructure.

- Add explicit permissions blocks to all workflow jobs (security)
- Add missing test assertions in auto-labeling.test.ts
- Add missing test assertions in outbound-tracking.test.ts
- Fix silent early return in draft-cleanup.test.ts
- Improve label assertion in full-reply-cycle.test.ts

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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 (3)
apps/web/__tests__/e2e/flows/outbound-tracking.test.ts (2)

94-95: Replace hardcoded timeout with polling or a named constant.

The 5-second wait uses a magic number and assumes processing completes within that time. Consider either:

  1. Adding a polling helper that waits for a specific condition (e.g., waitForOutboundProcessing)
  2. Using a constant from TIMEOUTS for consistency with the rest of the test suite

Hardcoded waits can lead to flaky tests when processing takes longer than expected.

Suggested approaches

Option 1: Use a constant

-      // Wait a bit for async processing
-      await new Promise((resolve) => setTimeout(resolve, 5000));
+      // Wait for async processing
+      await new Promise((resolve) => setTimeout(resolve, TIMEOUTS.PROCESSING_DELAY));

Option 2: Create a polling helper (preferred for robustness)

// In helpers/polling.ts
export async function waitForOutboundProcessing(delay: number) {
  // Poll for specific condition or use exponential backoff
  await new Promise((resolve) => setTimeout(resolve, delay));
}

152-152: Replace hardcoded 10-second waits with polling or named constants.

Both tests use identical 10-second arbitrary waits for processing to complete. This pattern repeats across the file with inconsistent durations (5s in test 1, 10s here), suggesting the actual required delay is uncertain.

Consider:

  1. Adding a TIMEOUTS.PROCESSING_DELAY constant for consistency
  2. Creating polling helpers that actively check for completion conditions rather than blindly waiting
  3. Using exponential backoff if the processing time varies significantly

Active polling would make tests both faster (when processing completes early) and more reliable (when processing takes longer).

Example polling approach
// Helper to poll for ExecutedRule absence
async function waitForNoExecutedRules(
  emailAccountId: string,
  messageId: string,
  maxWait: number = TIMEOUTS.PROCESSING_DELAY
) {
  const startTime = Date.now();
  while (Date.now() - startTime < maxWait) {
    const rules = await prisma.executedRule.findMany({
      where: { emailAccountId, messageId },
    });
    if (rules.length === 0) return;
    await new Promise((resolve) => setTimeout(resolve, 1000));
  }
}

// Usage in test
await waitForNoExecutedRules(outlook.id, reply.messageId);

Also applies to: 218-218

.github/workflows/e2e-flows.yml (1)

75-86: Use the dynamically determined store path for caching.

The STORE_PATH is determined dynamically but the cache step hardcodes ~/.pnpm-store. These may differ, causing cache misses.

🔧 Proposed fix
       - name: Setup pnpm cache
         uses: actions/cache@v4
         with:
-          path: ~/.pnpm-store
+          path: ${{ env.STORE_PATH }}
           key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
           restore-keys: |
             ${{ runner.os }}-pnpm-store-
📜 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 15adcc4 and a942c18.

📒 Files selected for processing (5)
  • .github/workflows/e2e-flows.yml
  • apps/web/__tests__/e2e/flows/auto-labeling.test.ts
  • apps/web/__tests__/e2e/flows/draft-cleanup.test.ts
  • apps/web/__tests__/e2e/flows/full-reply-cycle.test.ts
  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/web/tests/e2e/flows/auto-labeling.test.ts
  • apps/web/tests/e2e/flows/draft-cleanup.test.ts
  • apps/web/tests/e2e/flows/full-reply-cycle.test.ts
🧰 Additional context used
📓 Path-based instructions (20)
!(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:

  • .github/workflows/e2e-flows.yml
  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
**/*.{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/__tests__/e2e/flows/outbound-tracking.test.ts
apps/web/__tests__/**/*.test.ts

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

apps/web/__tests__/**/*.test.ts: Place all LLM-related tests in apps/web/__tests__/ directory
Use vitest imports (describe, expect, test, vi, beforeEach) in LLM test files
Mock 'server-only' module with empty object in LLM test files: vi.mock("server-only", () => ({}))
Set timeout constant const TIMEOUT = 15_000; for LLM tests
Use describe.runIf(isAiTest) with environment variable RUN_AI_TESTS === "true" to conditionally run LLM tests
Use console.debug() for outputting generated LLM content in tests, e.g., console.debug("Generated content:\n", result.content);
Prefer using existing helpers from @/__tests__/helpers.ts (getEmailAccount, getEmail, getRule, getMockMessage, getMockExecutedRule) instead of creating custom test data helpers

Files:

  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
apps/web/{utils/ai,utils/llms,__tests__}/**/*.ts

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

LLM-related code must be organized in specific directories: apps/web/utils/ai/ for main implementations, apps/web/utils/llms/ for core utilities and configurations, and apps/web/__tests__/ for LLM-specific tests

Files:

  • apps/web/__tests__/e2e/flows/outbound-tracking.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/__tests__/e2e/flows/outbound-tracking.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
Do not export types/interfaces that are only used within the same file. Export later if needed

Files:

  • apps/web/__tests__/e2e/flows/outbound-tracking.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/__tests__/e2e/flows/outbound-tracking.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/__tests__/e2e/flows/outbound-tracking.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/__tests__/e2e/flows/outbound-tracking.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/__tests__/e2e/flows/outbound-tracking.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/__tests__/e2e/flows/outbound-tracking.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/__tests__/e2e/flows/outbound-tracking.test.ts
**/{scripts,tests,__tests__}/**/*.{ts,tsx}

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

Use createScopedLogger only for code that doesn't run within a middleware chain, such as standalone scripts or tests

Files:

  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
apps/web/**/*.{ts,tsx,js,jsx}

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

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

Files:

  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
apps/web/**/*.{ts,tsx,js,jsx,json,css}

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

Format code with Prettier

Files:

  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
apps/web/**/*.test.{ts,tsx}

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

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

Files:

  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
apps/web/**/*.{example,ts,json}

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

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

Files:

  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
**/*.test.{ts,tsx}

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

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

Files:

  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
**/__tests__/**/*.{ts,tsx}

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

Place AI tests in the __tests__ directory and do not run them by default as they use a real LLM

Files:

  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
**/*.test.{js,jsx,ts,tsx}

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

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

Files:

  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
🧠 Learnings (7)
📓 Common learnings
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.775Z
Learning: Applies to **/*.test.{ts,tsx} : Use test helper functions `getEmail`, `getEmailAccount`, and `getRule` from `@/__tests__/helpers` for creating mock data
📚 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/__tests__/e2e/flows/outbound-tracking.test.ts
📚 Learning: 2026-01-01T10:42:29.775Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.775Z
Learning: Applies to **/*.test.{ts,tsx} : Use test helper functions `getEmail`, `getEmailAccount`, and `getRule` from `@/__tests__/helpers` for creating mock data

Applied to files:

  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
📚 Learning: 2025-12-21T12:21:37.794Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2025-12-21T12:21:37.794Z
Learning: Applies to apps/web/**/*.test.{ts,tsx} : Co-locate test files next to source files (e.g., `utils/example.test.ts`). Only E2E and AI tests go in `__tests__/`

Applied to files:

  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
📚 Learning: 2025-11-25T14:37:56.430Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Use vitest imports (`describe`, `expect`, `test`, `vi`, `beforeEach`) in LLM test files

Applied to files:

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

Applied to files:

  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{test,spec}.{js,jsx,ts,tsx} : Make sure the assertion function, like expect, is placed inside an it() function call

Applied to files:

  • apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
🔇 Additional comments (5)
.github/workflows/e2e-flows.yml (5)

27-31: LGTM!

Good use of concurrency with cancel-in-progress: false to prevent test account conflicts while ensuring running tests complete.


32-50: LGTM!

The check-enabled job correctly gates the workflow with explicit permissions and clear output for downstream jobs.


141-148: LGTM!

The ngrok tunnel setup with URL extraction via the local API is a clean approach for establishing the webhook endpoint.


189-197: Good failure handling.

Uploading logs on failure with appropriate retention is helpful for debugging flaky E2E tests.


199-225: LGTM!

The notify-disabled job provides useful guidance on required secrets for anyone setting up E2E flows.

- Improve health check reliability with proper error handling
- Add language specifiers to README code blocks
- Add assertion for draft creation in multiple-draft test
- Clean up redundant conditional in draft cleanup test

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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 (3)
.github/workflows/e2e-flows.yml (3)

75-86: Consider using the computed pnpm store path for cache.

The workflow computes the actual pnpm store path and stores it in STORE_PATH (line 78), but then uses a hardcoded ~/.pnpm-store path for the cache (line 83). This inconsistency could lead to cache misses if pnpm's actual store location differs.

♻️ Proposed fix
       - name: Setup pnpm cache
         uses: actions/cache@v4
         with:
-          path: ~/.pnpm-store
+          path: ${{ env.STORE_PATH }}
           key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
           restore-keys: |
             ${{ runner.os }}-pnpm-store-

Alternatively, if the hardcoded path is intentional, remove the unused "Get pnpm store directory" step (lines 75-78).


147-154: Add validation for ngrok tunnel URL extraction.

The workflow extracts the ngrok URL but doesn't verify that the extraction succeeded or that the URL is valid. If ngrok fails to start or returns unexpected JSON, NGROK_URL could be empty, causing tests to run with an invalid NEXT_PUBLIC_BASE_URL and producing cryptic failures.

♻️ Proposed fix
       - name: Start ngrok tunnel
         run: |
           ngrok http 3000 --log=stdout > ngrok.log 2>&1 &
           sleep 5
           # Extract the public URL
           NGROK_URL=$(curl -s http://localhost:4040/api/tunnels | jq -r '.tunnels[0].public_url')
+          if [ -z "$NGROK_URL" ] || [ "$NGROK_URL" = "null" ]; then
+            echo "ERROR: Failed to get ngrok tunnel URL"
+            cat ngrok.log
+            exit 1
+          fi
           echo "NGROK_URL=$NGROK_URL" >> $GITHUB_ENV
           echo "Tunnel URL: $NGROK_URL"

156-162: Consider validating test_file input.

When a test_file is provided via workflow_dispatch, the workflow passes it directly to the test command without validation. While invalid input will cause an obvious test failure, adding basic validation could provide clearer error messages for manual runs.

♻️ Optional validation example
       - name: Run E2E Flow Tests
         run: |
           if [ -n "${{ github.event.inputs.test_file }}" ]; then
+            TEST_FILE="apps/web/__tests__/e2e/flows/${{ github.event.inputs.test_file }}.test.ts"
+            if [ ! -f "$TEST_FILE" ]; then
+              echo "ERROR: Test file not found: $TEST_FILE"
+              exit 1
+            fi
             pnpm -F inbox-zero-ai test-e2e:flows ${{ github.event.inputs.test_file }}
           else
             pnpm -F inbox-zero-ai test-e2e:flows
           fi

This is optional since test runner errors are typically clear enough for manual workflow runs.

📜 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 a942c18 and b3812d3.

📒 Files selected for processing (3)
  • .github/workflows/e2e-flows.yml
  • apps/web/__tests__/e2e/flows/README.md
  • apps/web/__tests__/e2e/flows/draft-cleanup.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/web/tests/e2e/flows/draft-cleanup.test.ts
  • apps/web/tests/e2e/flows/README.md
🧰 Additional context used
📓 Path-based instructions (1)
!(pages/_document).{jsx,tsx}

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

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

Files:

  • .github/workflows/e2e-flows.yml
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.775Z
Learning: Applies to **/*.test.{ts,tsx} : Use test helper functions `getEmail`, `getEmailAccount`, and `getRule` from `@/__tests__/helpers` for creating mock data
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: test
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (1)
.github/workflows/e2e-flows.yml (1)

106-124: Verify health check endpoint is sufficient for E2E test readiness.

The health check polls http://localhost:3000 (root path) instead of the /api/health endpoint. While this addresses the HEALTH_API_KEY authentication issue mentioned in previous reviews, please verify that checking the root path reliably confirms the app server is fully initialized and ready to handle E2E tests (webhook endpoints, database connections, etc.).

If the root path doesn't guarantee full app readiness, consider either:

  1. Adding HEALTH_API_KEY to the env block and using the /api/health endpoint with proper authentication, or
  2. Creating a dedicated unauthenticated readiness endpoint for CI environments.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In @apps/web/__tests__/e2e/flows/README.md:
- Line 173: The README contains bare URLs (e.g., "http://localhost:3000") that
trigger MD034; update the text around the test account instructions to wrap each
bare URL in angle brackets (e.g., <http://localhost:3000>) or convert them to
Markdown links so the links on lines referencing the local dev site (lines
around the "visit http://localhost:3000" sentence) comply with linting.

In @packages/cli/src/main.ts:
- Around line 218-221: configName is used directly to build envFileName and
passed into resolve for envFile, risking path traversal; sanitize configName
before use (e.g., strip/replace path separators and any ".." segments, or
restrict to a safe regex like alphanumeric/underscore/dash, and fallback to
empty/undefined if invalid) and then compute envFileName = sanitized ?
`.env.${sanitized}` : ".env" and call resolve(REPO_ROOT ? resolve(REPO_ROOT,
"apps/web", envFileName) : resolve(STANDALONE_CONFIG_DIR, envFileName)) using
the sanitized value; ensure functions/variables to update are envFileName,
configName usage, and the envFile resolve call.
- Around line 75-76: The --name option is used to build filenames (e.g.
".env.<name>") but currently accepts arbitrary input; update the runSetup
function to validate and sanitize the name: reject or sanitize names containing
path separators or traversal sequences ("..", "/", "\"), control characters,
leading dots, or other unsafe characters and enforce a safe whitelist (e.g.
alphanumerics, hyphen, underscore, dot with a reasonable length limit); if
validation fails, log a clear error and exit non-zero rather than proceeding to
create a path. Use a strict regex check and/or path normalization to verify the
final constructed filename equals the expected safe basename before writing.
🧹 Nitpick comments (1)
apps/web/vitest.config.mts (1)

5-14: Add error handling for missing environment files.

The config() call could fail or return undefined if the environment file doesn't exist, potentially causing cryptic test failures. Consider adding validation or error handling.

🛡️ Proposed fix with error handling
+import { existsSync } from "node:fs";
 import { config } from "dotenv";
 import { defineConfig } from "vitest/config";
 import tsconfigPaths from "vite-tsconfig-paths";
 
 const isE2E = process.env.RUN_E2E_FLOW_TESTS === "true";
 const envFile = isE2E ? "./.env.e2e" : "./.env.test";
 
+// Validate env file exists before loading
+if (!existsSync(envFile)) {
+  console.warn(`Warning: Environment file ${envFile} not found. Using default environment.`);
+}
+
 export default defineConfig({
   plugins: [tsconfigPaths()],
   test: {
     environment: "node",
     setupFiles: ["./__tests__/setup.ts"],
     env: {
-      ...config({ path: envFile }).parsed,
+      ...(config({ path: envFile }).parsed ?? {}),
     },
   },
 });
📜 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 b3812d3 and f52cb34.

📒 Files selected for processing (4)
  • apps/web/__tests__/e2e/flows/README.md
  • apps/web/vitest.config.mts
  • package.json
  • packages/cli/src/main.ts
🧰 Additional context used
📓 Path-based instructions (9)
!(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/__tests__/e2e/flows/README.md
  • apps/web/vitest.config.mts
  • package.json
  • packages/cli/src/main.ts
**/package.json

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

Use pnpm as the package manager

Files:

  • package.json
**/*.{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:

  • packages/cli/src/main.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:

  • packages/cli/src/main.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:

  • packages/cli/src/main.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:

  • packages/cli/src/main.ts
**/*.{tsx,ts,css}

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

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

Files:

  • packages/cli/src/main.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:

  • packages/cli/src/main.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:

  • packages/cli/src/main.ts
🧠 Learnings (12)
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Use correct ISO language/country codes for the `lang` attribute

Applied to files:

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

Applied to files:

  • apps/web/__tests__/e2e/flows/README.md
  • apps/web/vitest.config.mts
📚 Learning: 2025-11-25T14:37:56.430Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Use `describe.runIf(isAiTest)` with environment variable `RUN_AI_TESTS === "true"` to conditionally run LLM tests

Applied to files:

  • apps/web/vitest.config.mts
📚 Learning: 2025-11-25T14:37:56.430Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Use vitest imports (`describe`, `expect`, `test`, `vi`, `beforeEach`) in LLM test files

Applied to files:

  • apps/web/vitest.config.mts
📚 Learning: 2026-01-01T10:42:29.775Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.775Z
Learning: Applies to **/*.test.{ts,tsx} : Use `vitest` as the testing framework

Applied to files:

  • apps/web/vitest.config.mts
📚 Learning: 2026-01-08T15:09:16.739Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2026-01-08T15:09:16.739Z
Learning: Applies to apps/web/**/*.test.{ts,tsx,js,jsx} : Co-locate test files next to source files (e.g., `utils/example.test.ts`). Only E2E and AI tests go in `__tests__/`

Applied to files:

  • apps/web/vitest.config.mts
📚 Learning: 2025-11-25T14:36:45.807Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/environment-variables.mdc:0-0
Timestamp: 2025-11-25T14:36:45.807Z
Learning: Applies to apps/web/env.ts : Add client-side environment variables to `apps/web/env.ts` under the `experimental__runtimeEnv` object to enable runtime access

Applied to files:

  • apps/web/vitest.config.mts
📚 Learning: 2026-01-04T00:25:22.311Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/notes.mdc:0-0
Timestamp: 2026-01-04T00:25:22.311Z
Learning: Applies to **/*.test.{js,jsx,ts,tsx} : Co-locate test files next to source files (e.g., `utils/example.test.ts`). Only E2E and AI tests go in `__tests__/`

Applied to files:

  • apps/web/vitest.config.mts
📚 Learning: 2026-01-01T10:42:29.775Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.775Z
Learning: Applies to **/__tests__/**/*.{ts,tsx} : Place AI tests in the `__tests__` directory and do not run them by default as they use a real LLM

Applied to files:

  • apps/web/vitest.config.mts
📚 Learning: 2026-01-08T15:09:16.739Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: apps/web/CLAUDE.md:0-0
Timestamp: 2026-01-08T15:09:16.739Z
Learning: Applies to apps/web/**/{.env.example,env.ts,turbo.json} : Add environment variables to `.env.example`, `env.ts`, and `turbo.json`

Applied to files:

  • apps/web/vitest.config.mts
📚 Learning: 2025-11-25T14:36:43.454Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/environment-variables.mdc:0-0
Timestamp: 2025-11-25T14:36:43.454Z
Learning: Applies to apps/web/env.ts : Define environment variables in `apps/web/env.ts` using Zod schema validation, organizing them into `server` and `client` sections

Applied to files:

  • apps/web/vitest.config.mts
📚 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 : Place all LLM-related tests in `apps/web/__tests__/` directory

Applied to files:

  • apps/web/vitest.config.mts
🪛 markdownlint-cli2 (0.18.1)
apps/web/__tests__/e2e/flows/README.md

173-173: Bare URL used

(MD034, no-bare-urls)


177-177: Bare URL used

(MD034, no-bare-urls)

⏰ 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: Baz Reviewer
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (3)
apps/web/__tests__/e2e/flows/README.md (2)

1-87: Well-structured and comprehensive E2E test documentation.

The README provides clear setup instructions, helpful test structure diagrams, detailed scenario descriptions, and practical troubleshooting guidance. The progression from overview through setup, running tests, test structure, and debugging is logical and easy to follow. The local setup guide is particularly helpful for contributors.


47-47: Clarify the distinction between GitHub Actions repository variables and environment variables.

The documentation references E2E_FLOWS_ENABLED (line 47, a GitHub Actions repository variable set in Settings) and RUN_E2E_FLOW_TESTS (line 54, an environment variable set via command line). These serve different purposes:

  • E2E_FLOWS_ENABLED: Gates whether the workflow runs in GitHub Actions
  • RUN_E2E_FLOW_TESTS: Passed at test execution to conditionally load config and run tests

Clarify that E2E_FLOWS_ENABLED is set in GitHub's Settings > Variables, while RUN_E2E_FLOW_TESTS is passed as an environment variable.

Also applies to: 54-54

package.json (1)

13-14: LGTM! CLI scripts are well-structured.

The new start:cli script follows the same pattern as the existing setup script and correctly references the CLI entry point.

Comment on lines +75 to 76
.option("-n, --name <name>", "Configuration name (creates .env.<name>)")
.action(runSetup);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Add input validation for the configuration name parameter.

The --name option accepts arbitrary user input without validation, which could lead to path traversal vulnerabilities when the name is used to construct file paths.

🔒 Recommended fix to add input validation
  program
    .command("setup")
    .description("Interactive setup for Inbox Zero")
    .option("-n, --name <name>", "Configuration name (creates .env.<name>)")
    .action(runSetup);

Add validation in the runSetup function:

 async function runSetup(options: { name?: string }) {
+  // Validate configuration name to prevent path traversal
+  if (options.name) {
+    const namePattern = /^[a-zA-Z0-9_-]+$/;
+    if (!namePattern.test(options.name) || options.name.length > 50) {
+      p.log.error(
+        "Invalid configuration name. Use only alphanumeric characters, hyphens, and underscores (max 50 chars)."
+      );
+      process.exit(1);
+    }
+  }
+
   const configName = options.name;

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

🤖 Prompt for AI Agents
In @packages/cli/src/main.ts around lines 75 - 76, The --name option is used to
build filenames (e.g. ".env.<name>") but currently accepts arbitrary input;
update the runSetup function to validate and sanitize the name: reject or
sanitize names containing path separators or traversal sequences ("..", "/",
"\"), control characters, leading dots, or other unsafe characters and enforce a
safe whitelist (e.g. alphanumerics, hyphen, underscore, dot with a reasonable
length limit); if validation fails, log a clear error and exit non-zero rather
than proceeding to create a path. Use a strict regex check and/or path
normalization to verify the final constructed filename equals the expected safe
basename before writing.

Comment on lines +218 to +221
const envFileName = configName ? `.env.${configName}` : ".env";
const envFile = REPO_ROOT
? resolve(REPO_ROOT, "apps/web/.env")
: STANDALONE_ENV_FILE;
? resolve(REPO_ROOT, "apps/web", envFileName)
: resolve(STANDALONE_CONFIG_DIR, envFileName);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Sanitize the configuration name before using it in file paths.

The configName is directly interpolated into file paths without sanitization. Even though validation should occur earlier, defensive coding practices suggest sanitizing at the point of use to prevent path traversal attacks.

🛡️ Add defensive sanitization
  // Determine paths - if in repo, write to apps/web/.env, otherwise use standalone
  const configDir = REPO_ROOT ?? STANDALONE_CONFIG_DIR;
+ // Ensure configName doesn't contain path separators (defense in depth)
+ const sanitizedName = configName?.replace(/[^a-zA-Z0-9_-]/g, '');
- const envFileName = configName ? `.env.${configName}` : ".env";
+ const envFileName = sanitizedName ? `.env.${sanitizedName}` : ".env";
  const envFile = REPO_ROOT
    ? resolve(REPO_ROOT, "apps/web", envFileName)
    : resolve(STANDALONE_CONFIG_DIR, envFileName);

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

🤖 Prompt for AI Agents
In @packages/cli/src/main.ts around lines 218 - 221, configName is used directly
to build envFileName and passed into resolve for envFile, risking path
traversal; sanitize configName before use (e.g., strip/replace path separators
and any ".." segments, or restrict to a safe regex like
alphanumeric/underscore/dash, and fallback to empty/undefined if invalid) and
then compute envFileName = sanitized ? `.env.${sanitized}` : ".env" and call
resolve(REPO_ROOT ? resolve(REPO_ROOT, "apps/web", envFileName) :
resolve(STANDALONE_CONFIG_DIR, envFileName)) using the sanitized value; ensure
functions/variables to update are envFileName, configName usage, and the envFile
resolve call.

Copy link
Contributor

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

Choose a reason for hiding this comment

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

4 issues found across 4 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="packages/cli/src/main.ts">

<violation number="1" location="packages/cli/src/main.ts:75">
P1: The `--name` option creates named env files (e.g., `.env.production`) but the `start`, `stop`, `logs`, `status`, and `update` commands only recognize the default `.env` file. This breaks the CLI workflow for users who create named configurations.</violation>
</file>

<file name="apps/web/__tests__/e2e/flows/README.md">

<violation number="1" location="apps/web/__tests__/e2e/flows/README.md:151">
P2: Inconsistent package manager usage. Steps 1-2 use 'npm' but step 5 uses 'pnpm'. Since the repository specifies pnpm@10.27.0 as its package manager, use 'pnpm' consistently throughout the guide.</violation>

<violation number="2" location="apps/web/__tests__/e2e/flows/README.md:154">
P2: Inconsistent package manager usage. This step uses 'npm' but step 5 uses 'pnpm'. Since the repository specifies pnpm@10.27.0 as its package manager, use 'pnpm' consistently throughout the guide.</violation>

<violation number="3" location="apps/web/__tests__/e2e/flows/README.md:173">
P2: Inconsistent package manager usage. This troubleshooting tip uses 'npm run start:cli' while other commands use 'pnpm'. Use 'pnpm start:cli' for consistency with the repository's package manager.</violation>
</file>

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

program
.command("setup")
.description("Interactive setup for Inbox Zero")
.option("-n, --name <name>", "Configuration name (creates .env.<name>)")
Copy link
Contributor

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

Choose a reason for hiding this comment

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

P1: The --name option creates named env files (e.g., .env.production) but the start, stop, logs, status, and update commands only recognize the default .env file. This breaks the CLI workflow for users who create named configurations.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/cli/src/main.ts, line 75:

<comment>The `--name` option creates named env files (e.g., `.env.production`) but the `start`, `stop`, `logs`, `status`, and `update` commands only recognize the default `.env` file. This breaks the CLI workflow for users who create named configurations.</comment>

<file context>
@@ -72,6 +72,7 @@ async function main() {
   program
     .command("setup")
     .description("Interactive setup for Inbox Zero")
+    .option("-n, --name <name>", "Configuration name (creates .env.<name>)")
     .action(runSetup);
 
</file context>
Fix with Cubic

Comment on lines 216 to +221
// Determine paths - if in repo, write to apps/web/.env, otherwise use standalone
const configDir = REPO_ROOT ?? STANDALONE_CONFIG_DIR;
const envFileName = configName ? `.env.${configName}` : ".env";
const envFile = REPO_ROOT
? resolve(REPO_ROOT, "apps/web/.env")
: STANDALONE_ENV_FILE;
? resolve(REPO_ROOT, "apps/web", envFileName)
: resolve(STANDALONE_CONFIG_DIR, envFileName);
Copy link
Contributor

Choose a reason for hiding this comment

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

runSetup now writes to .env.<name> in both repo and standalone modes (lines 216‑221), but nothing in this change hooks that new path into the start command. runStart still uses the default STANDALONE_ENV_FILE/compose invocation, so after setup --name e2e the CLI still starts Docker Compose with the base .env, ignoring the named configuration and defeating the new feature. Start needs to accept or infer the chosen config name (e.g., pass --env-file ~/.inbox-zero/.env.<name> when running compose) before named environments can actually be used.

Prompt for AI Agents:

In packages/cli/src/main.ts around lines 216 to 221 (and the start command registration
near lines ~66-76), runSetup was changed to write to .env.<name> but runStart still uses
the default standalone env/compose path. Update the CLI so the start command accepts the
same --name / -n option as setup (or a dedicated --config-name), propagate that option
into runStart, and in runStart compute the env file path the same way runSetup does (use
REPO_ROOT ? resolve(REPO_ROOT, "apps/web", envFileName) : resolve(STANDALONE_CONFIG_DIR,
envFileName)). When invoking Docker Compose in runStart, pass the computed env file to
the compose command (for example add --env-file <path> or the appropriate compose CLI
flag) so named configs are actually used; keep behavior unchanged when no name is
provided. Ensure argument parsing, help text, and error handling mirror the setup
command.

Fix in Cursor


Finding type: Logical Bugs

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @apps/web/__tests__/e2e/flows/README.md:
- Line 176: Update the README lines that contain bare URLs by wrapping them in
markdown formatting: replace the plain "http://localhost:3000" occurrences
(lines referencing the local dev server) with either an inline code span
(`http://localhost:3000`) or a markdown link like
[http://localhost:3000](http://localhost:3000); do the same for the other bare
URL on the referenced line so both URLs are consistently wrapped.
📜 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 f52cb34 and 84c1dc2.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (2)
  • apps/web/__tests__/e2e/flows/README.md
  • apps/web/package.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/package.json
🧰 Additional context used
📓 Path-based instructions (1)
!(pages/_document).{jsx,tsx}

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

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

Files:

  • apps/web/__tests__/e2e/flows/README.md
🧠 Learnings (4)
📓 Common learnings
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2026-01-01T10:42:29.775Z
Learning: Applies to **/*.test.{ts,tsx} : Use `vitest` as the testing framework
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-11-25T14:37:56.430Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Use vitest imports (`describe`, `expect`, `test`, `vi`, `beforeEach`) in LLM test files
📚 Learning: 2025-11-25T14:42:08.869Z
Learnt from: CR
Repo: elie222/inbox-zero PR: 0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-11-25T14:42:08.869Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Use correct ISO language/country codes for the `lang` attribute

Applied to files:

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

Applied to files:

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

Applied to files:

  • apps/web/__tests__/e2e/flows/README.md
🪛 markdownlint-cli2 (0.18.1)
apps/web/__tests__/e2e/flows/README.md

176-176: Bare URL used

(MD034, no-bare-urls)


180-180: Bare URL used

(MD034, no-bare-urls)

⏰ 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: Baz Reviewer
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (2)
apps/web/__tests__/e2e/flows/README.md (2)

37-37: The referenced documentation file exists at ./docs/hosting/environment-variables.md. The link is valid and no action is needed.


151-151: [rewritten review comment]
[classification tag]

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