feat: add comprehensive E2E flow tests for email processing#1222
feat: add comprehensive E2E flow tests for email processing#1222
Conversation
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>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughAdds 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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ 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)
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. Comment |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
envto 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 inafter()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
afterEachhook 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
📒 Files selected for processing (15)
.github/workflows/e2e-flows.ymlapps/web/__tests__/e2e/flows/README.mdapps/web/__tests__/e2e/flows/auto-labeling.test.tsapps/web/__tests__/e2e/flows/config.tsapps/web/__tests__/e2e/flows/draft-cleanup.test.tsapps/web/__tests__/e2e/flows/full-reply-cycle.test.tsapps/web/__tests__/e2e/flows/helpers/accounts.tsapps/web/__tests__/e2e/flows/helpers/email.tsapps/web/__tests__/e2e/flows/helpers/logging.tsapps/web/__tests__/e2e/flows/helpers/polling.tsapps/web/__tests__/e2e/flows/helpers/webhook.tsapps/web/__tests__/e2e/flows/outbound-tracking.test.tsapps/web/__tests__/e2e/flows/setup.tsapps/web/__tests__/e2e/flows/teardown.tsapps/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 theswrpackage
Useresult?.serverErrorwithtoastErrorfrom@/components/Toastfor 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 conventionuse[FeatureName]Enabledthat return a boolean fromuseFeatureFlagEnabled("flag-key")
For A/B test variant flags, create hooks using the naming conventionuse[FeatureName]Variantthat define variant types, useuseFeatureFlagVariantKey()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
Useas constinstead of literal types and type annotations
Use eitherT[]orArray<T>consistently
Initialize each enum member value explicitly
Useexport typefor types
Use `impo...
Files:
apps/web/__tests__/e2e/flows/full-reply-cycle.test.tsapps/web/__tests__/e2e/flows/helpers/webhook.tsapps/web/__tests__/e2e/flows/auto-labeling.test.tsapps/web/__tests__/e2e/flows/draft-cleanup.test.tsapps/web/__tests__/e2e/flows/outbound-tracking.test.tsapps/web/__tests__/e2e/flows/teardown.tsapps/web/__tests__/e2e/flows/helpers/accounts.tsapps/web/__tests__/e2e/flows/config.tsapps/web/__tests__/e2e/flows/setup.tsapps/web/__tests__/e2e/flows/helpers/logging.tsapps/web/__tests__/e2e/flows/helpers/polling.tsapps/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 inapps/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 constantconst TIMEOUT = 15_000;for LLM tests
Usedescribe.runIf(isAiTest)with environment variableRUN_AI_TESTS === "true"to conditionally run LLM tests
Useconsole.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.tsapps/web/__tests__/e2e/flows/auto-labeling.test.tsapps/web/__tests__/e2e/flows/draft-cleanup.test.tsapps/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, andapps/web/__tests__/for LLM-specific tests
Files:
apps/web/__tests__/e2e/flows/full-reply-cycle.test.tsapps/web/__tests__/e2e/flows/helpers/webhook.tsapps/web/__tests__/e2e/flows/auto-labeling.test.tsapps/web/__tests__/e2e/flows/draft-cleanup.test.tsapps/web/__tests__/e2e/flows/outbound-tracking.test.tsapps/web/__tests__/e2e/flows/teardown.tsapps/web/__tests__/e2e/flows/helpers/accounts.tsapps/web/__tests__/e2e/flows/config.tsapps/web/__tests__/e2e/flows/setup.tsapps/web/__tests__/e2e/flows/helpers/logging.tsapps/web/__tests__/e2e/flows/helpers/polling.tsapps/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/enumsinstead of@/generated/prisma/clientto avoid Next.js bundling errors in client componentsImport Prisma using the project's centralized utility:
import prisma from '@/utils/prisma'
Files:
apps/web/__tests__/e2e/flows/full-reply-cycle.test.tsapps/web/__tests__/e2e/flows/helpers/webhook.tsapps/web/__tests__/e2e/flows/auto-labeling.test.tsapps/web/__tests__/e2e/flows/draft-cleanup.test.tsapps/web/__tests__/e2e/flows/outbound-tracking.test.tsapps/web/__tests__/e2e/flows/teardown.tsapps/web/__tests__/e2e/flows/helpers/accounts.tsapps/web/__tests__/e2e/flows/config.tsapps/web/__tests__/e2e/flows/setup.tsapps/web/__tests__/e2e/flows/helpers/logging.tsapps/web/__tests__/e2e/flows/helpers/polling.tsapps/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.tsapps/web/__tests__/e2e/flows/helpers/webhook.tsapps/web/__tests__/e2e/flows/auto-labeling.test.tsapps/web/__tests__/e2e/flows/draft-cleanup.test.tsapps/web/__tests__/e2e/flows/outbound-tracking.test.tsapps/web/__tests__/e2e/flows/teardown.tsapps/web/__tests__/e2e/flows/helpers/accounts.tsapps/web/__tests__/e2e/flows/config.tsapps/web/__tests__/e2e/flows/setup.tsapps/web/__tests__/e2e/flows/helpers/logging.tsapps/web/__tests__/e2e/flows/helpers/polling.tsapps/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'sselectoption. 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. AllfindUnique/findFirstcalls 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
AllfindManyqueries 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.tsapps/web/__tests__/e2e/flows/helpers/webhook.tsapps/web/__tests__/e2e/flows/auto-labeling.test.tsapps/web/__tests__/e2e/flows/draft-cleanup.test.tsapps/web/__tests__/e2e/flows/outbound-tracking.test.tsapps/web/__tests__/e2e/flows/teardown.tsapps/web/__tests__/e2e/flows/helpers/accounts.tsapps/web/__tests__/e2e/flows/config.tsapps/web/__tests__/e2e/flows/setup.tsapps/web/__tests__/e2e/flows/helpers/logging.tsapps/web/__tests__/e2e/flows/helpers/polling.tsapps/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
Usenext/imagepackage for images
For API GET requests to server, use theswrpackage with hooks likeuseSWRto fetch data
For text inputs, use theInputcomponent withregisterPropsfor form integration and error handling
Files:
apps/web/__tests__/e2e/flows/full-reply-cycle.test.tsapps/web/__tests__/e2e/flows/helpers/webhook.tsapps/web/__tests__/e2e/flows/auto-labeling.test.tsapps/web/__tests__/e2e/flows/draft-cleanup.test.tsapps/web/__tests__/e2e/flows/outbound-tracking.test.tsapps/web/__tests__/e2e/flows/teardown.tsapps/web/__tests__/e2e/flows/helpers/accounts.tsapps/web/__tests__/e2e/flows/config.tsapps/web/__tests__/e2e/flows/setup.tsapps/web/__tests__/e2e/flows/helpers/logging.tsapps/web/__tests__/e2e/flows/helpers/polling.tsapps/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.tsapps/web/__tests__/e2e/flows/helpers/webhook.tsapps/web/__tests__/e2e/flows/auto-labeling.test.tsapps/web/__tests__/e2e/flows/draft-cleanup.test.tsapps/web/__tests__/e2e/flows/outbound-tracking.test.tsapps/web/__tests__/e2e/flows/teardown.tsapps/web/__tests__/e2e/flows/helpers/accounts.tsapps/web/__tests__/e2e/flows/config.tsapps/web/__tests__/e2e/flows/setup.tsapps/web/__tests__/e2e/flows/helpers/logging.tsapps/web/__tests__/e2e/flows/helpers/polling.tsapps/web/__tests__/e2e/flows/helpers/email.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useaccessKeyattribute on any HTML element
Don't setaria-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 thescopeprop 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 assigntabIndexto non-interactive HTML elements
Don't use positive integers fortabIndexproperty
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 atitleelement 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
AssigntabIndexto non-interactive HTML elements witharia-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 atypeattribute for button elements
Make elements with interactive roles and handlers focusable
Give heading elements content that's accessible to screen readers (not hidden witharia-hidden)
Always include alangattribute on the html element
Always include atitleattribute for iframe elements
AccompanyonClickwith at least one of:onKeyUp,onKeyDown, oronKeyPress
AccompanyonMouseOver/onMouseOutwithonFocus/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.tsapps/web/__tests__/e2e/flows/helpers/webhook.tsapps/web/__tests__/e2e/flows/auto-labeling.test.tsapps/web/__tests__/e2e/flows/draft-cleanup.test.tsapps/web/__tests__/e2e/flows/outbound-tracking.test.tsapps/web/__tests__/e2e/flows/teardown.tsapps/web/__tests__/e2e/flows/helpers/accounts.tsapps/web/__tests__/e2e/flows/config.tsapps/web/__tests__/e2e/flows/setup.tsapps/web/__tests__/e2e/flows/helpers/logging.tsapps/web/__tests__/e2e/flows/helpers/polling.tsapps/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.tsapps/web/__tests__/e2e/flows/auto-labeling.test.tsapps/web/__tests__/e2e/flows/draft-cleanup.test.tsapps/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.tsapps/web/__tests__/e2e/flows/helpers/webhook.tsapps/web/__tests__/e2e/flows/auto-labeling.test.tsapps/web/__tests__/e2e/flows/README.mdapps/web/__tests__/e2e/flows/draft-cleanup.test.tsapps/web/__tests__/e2e/flows/outbound-tracking.test.tsapps/web/__tests__/e2e/flows/teardown.ts.github/workflows/e2e-flows.ymlapps/web/__tests__/e2e/flows/helpers/accounts.tsapps/web/__tests__/e2e/flows/config.tsapps/web/__tests__/e2e/flows/setup.tsapps/web/package.jsonapps/web/__tests__/e2e/flows/helpers/logging.tsapps/web/__tests__/e2e/flows/helpers/polling.tsapps/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.tsapps/web/__tests__/e2e/flows/helpers/webhook.tsapps/web/__tests__/e2e/flows/auto-labeling.test.tsapps/web/__tests__/e2e/flows/draft-cleanup.test.tsapps/web/__tests__/e2e/flows/outbound-tracking.test.tsapps/web/__tests__/e2e/flows/teardown.tsapps/web/__tests__/e2e/flows/helpers/accounts.tsapps/web/__tests__/e2e/flows/config.tsapps/web/__tests__/e2e/flows/setup.tsapps/web/__tests__/e2e/flows/helpers/logging.tsapps/web/__tests__/e2e/flows/helpers/polling.tsapps/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.tsapps/web/__tests__/e2e/flows/helpers/webhook.tsapps/web/__tests__/e2e/flows/auto-labeling.test.tsapps/web/__tests__/e2e/flows/draft-cleanup.test.tsapps/web/__tests__/e2e/flows/outbound-tracking.test.tsapps/web/__tests__/e2e/flows/teardown.tsapps/web/__tests__/e2e/flows/helpers/accounts.tsapps/web/__tests__/e2e/flows/config.tsapps/web/__tests__/e2e/flows/setup.tsapps/web/__tests__/e2e/flows/helpers/logging.tsapps/web/__tests__/e2e/flows/helpers/polling.tsapps/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.tsapps/web/__tests__/e2e/flows/helpers/webhook.tsapps/web/__tests__/e2e/flows/auto-labeling.test.tsapps/web/__tests__/e2e/flows/draft-cleanup.test.tsapps/web/__tests__/e2e/flows/outbound-tracking.test.tsapps/web/__tests__/e2e/flows/teardown.tsapps/web/__tests__/e2e/flows/helpers/accounts.tsapps/web/__tests__/e2e/flows/config.tsapps/web/__tests__/e2e/flows/setup.tsapps/web/__tests__/e2e/flows/helpers/logging.tsapps/web/__tests__/e2e/flows/helpers/polling.tsapps/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.tsapps/web/__tests__/e2e/flows/helpers/webhook.tsapps/web/__tests__/e2e/flows/auto-labeling.test.tsapps/web/__tests__/e2e/flows/draft-cleanup.test.tsapps/web/__tests__/e2e/flows/outbound-tracking.test.tsapps/web/__tests__/e2e/flows/teardown.tsapps/web/__tests__/e2e/flows/helpers/accounts.tsapps/web/__tests__/e2e/flows/config.tsapps/web/__tests__/e2e/flows/setup.tsapps/web/package.jsonapps/web/__tests__/e2e/flows/helpers/logging.tsapps/web/__tests__/e2e/flows/helpers/polling.tsapps/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.tsapps/web/__tests__/e2e/flows/auto-labeling.test.tsapps/web/__tests__/e2e/flows/draft-cleanup.test.tsapps/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, andturbo.json
Files:
apps/web/__tests__/e2e/flows/full-reply-cycle.test.tsapps/web/__tests__/e2e/flows/helpers/webhook.tsapps/web/__tests__/e2e/flows/auto-labeling.test.tsapps/web/__tests__/e2e/flows/draft-cleanup.test.tsapps/web/__tests__/e2e/flows/outbound-tracking.test.tsapps/web/__tests__/e2e/flows/teardown.tsapps/web/__tests__/e2e/flows/helpers/accounts.tsapps/web/__tests__/e2e/flows/config.tsapps/web/__tests__/e2e/flows/setup.tsapps/web/package.jsonapps/web/__tests__/e2e/flows/helpers/logging.tsapps/web/__tests__/e2e/flows/helpers/polling.tsapps/web/__tests__/e2e/flows/helpers/email.ts
**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/testing.mdc)
**/*.test.{ts,tsx}: Usevitestas the testing framework
Colocate test files next to the tested file with.test.tsor.test.tsxnaming convention (e.g.,dir/format.tsanddir/format.test.ts)
Mockserver-onlyusingvi.mock("server-only", () => ({}))
Mock Prisma usingvi.mock("@/utils/prisma")and the provided mock from@/utils/__mocks__/prisma
Use test helper functionsgetEmail,getEmailAccount, andgetRulefrom@/__tests__/helpersfor creating mock data
Clear all mocks between tests usingbeforeEach(() => { 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.tsapps/web/__tests__/e2e/flows/auto-labeling.test.tsapps/web/__tests__/e2e/flows/draft-cleanup.test.tsapps/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.tsapps/web/__tests__/e2e/flows/helpers/webhook.tsapps/web/__tests__/e2e/flows/auto-labeling.test.tsapps/web/__tests__/e2e/flows/draft-cleanup.test.tsapps/web/__tests__/e2e/flows/outbound-tracking.test.tsapps/web/__tests__/e2e/flows/teardown.tsapps/web/__tests__/e2e/flows/helpers/accounts.tsapps/web/__tests__/e2e/flows/config.tsapps/web/__tests__/e2e/flows/setup.tsapps/web/__tests__/e2e/flows/helpers/logging.tsapps/web/__tests__/e2e/flows/helpers/polling.tsapps/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.tsapps/web/__tests__/e2e/flows/auto-labeling.test.tsapps/web/__tests__/e2e/flows/draft-cleanup.test.tsapps/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.tsapps/web/__tests__/e2e/flows/helpers/accounts.tsapps/web/__tests__/e2e/flows/helpers/logging.tsapps/web/__tests__/e2e/flows/helpers/polling.tsapps/web/__tests__/e2e/flows/helpers/email.ts
**/package.json
📄 CodeRabbit inference engine (.cursor/rules/installing-packages.mdc)
Use
pnpmas 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/webworkspace 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.tsapps/web/__tests__/e2e/flows/helpers/webhook.tsapps/web/__tests__/e2e/flows/auto-labeling.test.tsapps/web/__tests__/e2e/flows/draft-cleanup.test.tsapps/web/__tests__/e2e/flows/outbound-tracking.test.tsapps/web/__tests__/e2e/flows/teardown.tsapps/web/__tests__/e2e/flows/helpers/accounts.tsapps/web/__tests__/e2e/flows/config.tsapps/web/__tests__/e2e/flows/setup.tsapps/web/__tests__/e2e/flows/helpers/logging.tsapps/web/__tests__/e2e/flows/helpers/polling.tsapps/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.tsapps/web/__tests__/e2e/flows/helpers/webhook.tsapps/web/__tests__/e2e/flows/auto-labeling.test.tsapps/web/__tests__/e2e/flows/README.mdapps/web/__tests__/e2e/flows/draft-cleanup.test.tsapps/web/__tests__/e2e/flows/outbound-tracking.test.tsapps/web/__tests__/e2e/flows/teardown.tsapps/web/__tests__/e2e/flows/helpers/accounts.tsapps/web/__tests__/e2e/flows/config.tsapps/web/__tests__/e2e/flows/setup.tsapps/web/__tests__/e2e/flows/helpers/logging.tsapps/web/__tests__/e2e/flows/helpers/polling.tsapps/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.tsapps/web/__tests__/e2e/flows/helpers/webhook.tsapps/web/__tests__/e2e/flows/auto-labeling.test.tsapps/web/__tests__/e2e/flows/draft-cleanup.test.tsapps/web/__tests__/e2e/flows/outbound-tracking.test.tsapps/web/__tests__/e2e/flows/teardown.tsapps/web/__tests__/e2e/flows/helpers/accounts.tsapps/web/__tests__/e2e/flows/config.tsapps/web/__tests__/e2e/flows/setup.tsapps/web/package.jsonapps/web/__tests__/e2e/flows/helpers/logging.tsapps/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.tsapps/web/__tests__/e2e/flows/auto-labeling.test.tsapps/web/__tests__/e2e/flows/draft-cleanup.test.tsapps/web/__tests__/e2e/flows/outbound-tracking.test.tsapps/web/__tests__/e2e/flows/teardown.tsapps/web/__tests__/e2e/flows/config.tsapps/web/package.jsonapps/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.tsapps/web/__tests__/e2e/flows/outbound-tracking.test.tsapps/web/__tests__/e2e/flows/teardown.tsapps/web/__tests__/e2e/flows/helpers/accounts.tsapps/web/__tests__/e2e/flows/helpers/polling.tsapps/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.tsapps/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.tsapps/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.tsapps/web/__tests__/e2e/flows/draft-cleanup.test.tsapps/web/__tests__/e2e/flows/config.tsapps/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.tsapps/web/__tests__/e2e/flows/teardown.tsapps/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.tsapps/web/__tests__/e2e/flows/outbound-tracking.test.tsapps/web/__tests__/e2e/flows/teardown.tsapps/web/__tests__/e2e/flows/helpers/accounts.tsapps/web/__tests__/e2e/flows/config.tsapps/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.tsapps/web/__tests__/e2e/flows/draft-cleanup.test.tsapps/web/__tests__/e2e/flows/config.tsapps/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.tsapps/web/__tests__/e2e/flows/outbound-tracking.test.tsapps/web/__tests__/e2e/flows/teardown.tsapps/web/__tests__/e2e/flows/config.tsapps/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.tsapps/web/__tests__/e2e/flows/teardown.tsapps/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.tsapps/web/__tests__/e2e/flows/helpers/accounts.tsapps/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.tsapps/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.tsapps/web/__tests__/e2e/flows/helpers/logging.tsapps/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.tsapps/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.tsapps/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-envfor cross-platform environment variable handling, scopes to the__tests__/e2e/flowsdirectory, 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>
There was a problem hiding this comment.
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:
- Adding a polling helper that waits for a specific condition (e.g.,
waitForOutboundProcessing)- Using a constant from
TIMEOUTSfor consistency with the rest of the test suiteHardcoded 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:
- Adding a
TIMEOUTS.PROCESSING_DELAYconstant for consistency- Creating polling helpers that actively check for completion conditions rather than blindly waiting
- 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_PATHis 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
📒 Files selected for processing (5)
.github/workflows/e2e-flows.ymlapps/web/__tests__/e2e/flows/auto-labeling.test.tsapps/web/__tests__/e2e/flows/draft-cleanup.test.tsapps/web/__tests__/e2e/flows/full-reply-cycle.test.tsapps/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.ymlapps/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 theswrpackage
Useresult?.serverErrorwithtoastErrorfrom@/components/Toastfor 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 conventionuse[FeatureName]Enabledthat return a boolean fromuseFeatureFlagEnabled("flag-key")
For A/B test variant flags, create hooks using the naming conventionuse[FeatureName]Variantthat define variant types, useuseFeatureFlagVariantKey()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
Useas constinstead of literal types and type annotations
Use eitherT[]orArray<T>consistently
Initialize each enum member value explicitly
Useexport typefor 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 inapps/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 constantconst TIMEOUT = 15_000;for LLM tests
Usedescribe.runIf(isAiTest)with environment variableRUN_AI_TESTS === "true"to conditionally run LLM tests
Useconsole.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, andapps/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/enumsinstead of@/generated/prisma/clientto avoid Next.js bundling errors in client componentsImport 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'sselectoption. 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. AllfindUnique/findFirstcalls 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
AllfindManyqueries 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
Usenext/imagepackage for images
For API GET requests to server, use theswrpackage with hooks likeuseSWRto fetch data
For text inputs, use theInputcomponent withregisterPropsfor 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 useaccessKeyattribute on any HTML element
Don't setaria-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 thescopeprop 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 assigntabIndexto non-interactive HTML elements
Don't use positive integers fortabIndexproperty
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 atitleelement 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
AssigntabIndexto non-interactive HTML elements witharia-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 atypeattribute for button elements
Make elements with interactive roles and handlers focusable
Give heading elements content that's accessible to screen readers (not hidden witharia-hidden)
Always include alangattribute on the html element
Always include atitleattribute for iframe elements
AccompanyonClickwith at least one of:onKeyUp,onKeyDown, oronKeyPress
AccompanyonMouseOver/onMouseOutwithonFocus/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, andturbo.json
Files:
apps/web/__tests__/e2e/flows/outbound-tracking.test.ts
**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/testing.mdc)
**/*.test.{ts,tsx}: Usevitestas the testing framework
Colocate test files next to the tested file with.test.tsor.test.tsxnaming convention (e.g.,dir/format.tsanddir/format.test.ts)
Mockserver-onlyusingvi.mock("server-only", () => ({}))
Mock Prisma usingvi.mock("@/utils/prisma")and the provided mock from@/utils/__mocks__/prisma
Use test helper functionsgetEmail,getEmailAccount, andgetRulefrom@/__tests__/helpersfor creating mock data
Clear all mocks between tests usingbeforeEach(() => { 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: falseto prevent test account conflicts while ensuring running tests complete.
32-50: LGTM!The
check-enabledjob 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-disabledjob 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>
There was a problem hiding this comment.
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-storepath 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_URLcould be empty, causing tests to run with an invalidNEXT_PUBLIC_BASE_URLand 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_fileis 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 fiThis 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
📒 Files selected for processing (3)
.github/workflows/e2e-flows.ymlapps/web/__tests__/e2e/flows/README.mdapps/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/healthendpoint. While this addresses theHEALTH_API_KEYauthentication 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:
- Adding
HEALTH_API_KEYto the env block and using the/api/healthendpoint with proper authentication, or- Creating a dedicated unauthenticated readiness endpoint for CI environments.
There was a problem hiding this comment.
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
📒 Files selected for processing (4)
apps/web/__tests__/e2e/flows/README.mdapps/web/vitest.config.mtspackage.jsonpackages/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.mdapps/web/vitest.config.mtspackage.jsonpackages/cli/src/main.ts
**/package.json
📄 CodeRabbit inference engine (.cursor/rules/installing-packages.mdc)
Use
pnpmas 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 theswrpackage
Useresult?.serverErrorwithtoastErrorfrom@/components/Toastfor 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 conventionuse[FeatureName]Enabledthat return a boolean fromuseFeatureFlagEnabled("flag-key")
For A/B test variant flags, create hooks using the naming conventionuse[FeatureName]Variantthat define variant types, useuseFeatureFlagVariantKey()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
Useas constinstead of literal types and type annotations
Use eitherT[]orArray<T>consistently
Initialize each enum member value explicitly
Useexport typefor 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/enumsinstead of@/generated/prisma/clientto avoid Next.js bundling errors in client componentsImport 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'sselectoption. 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. AllfindUnique/findFirstcalls 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
AllfindManyqueries 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
Usenext/imagepackage for images
For API GET requests to server, use theswrpackage with hooks likeuseSWRto fetch data
For text inputs, use theInputcomponent withregisterPropsfor 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 useaccessKeyattribute on any HTML element
Don't setaria-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 thescopeprop 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 assigntabIndexto non-interactive HTML elements
Don't use positive integers fortabIndexproperty
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 atitleelement 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
AssigntabIndexto non-interactive HTML elements witharia-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 atypeattribute for button elements
Make elements with interactive roles and handlers focusable
Give heading elements content that's accessible to screen readers (not hidden witharia-hidden)
Always include alangattribute on the html element
Always include atitleattribute for iframe elements
AccompanyonClickwith at least one of:onKeyUp,onKeyDown, oronKeyPress
AccompanyonMouseOver/onMouseOutwithonFocus/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.mdapps/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) andRUN_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 ActionsRUN_E2E_FLOW_TESTS: Passed at test execution to conditionally load config and run testsClarify 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:cliscript follows the same pattern as the existingsetupscript and correctly references the CLI entry point.
| .option("-n, --name <name>", "Configuration name (creates .env.<name>)") | ||
| .action(runSetup); |
There was a problem hiding this comment.
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.
| 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); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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>)") |
There was a problem hiding this comment.
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>
| // 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); |
There was a problem hiding this comment.
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.
Finding type: Logical Bugs
There was a problem hiding this comment.
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
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (2)
apps/web/__tests__/e2e/flows/README.mdapps/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]
User description
Summary
Test Scenarios
Test plan
pnpm test-e2e:flowslocally 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:13pxIntroduces 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.
Modified files (1)
Latest Contributors(2)
ngrokfor secure webhook delivery, updates topackage.jsonfor test scripts, modifications tovitest.config.mtsfor environment handling, and enhancements to the CLIsetupcommand for managing environment configurations. It also adds documentation for setting up and running these tests.Modified files (9)
Latest Contributors(0)
Modified files (9)
Latest Contributors(0)
Summary by CodeRabbit
Tests
Documentation
Chores
✏️ Tip: You can customize this high-level summary in your review settings.