Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughCentralized test timeouts into TIMEOUT constants and standardized test timeouts; bumped many dependency versions; made several private fields readonly; tightened LLM selection with an explicit error for unknown providers; adjusted date-filter branching; added optional emailHistoryContext to draft API; minor barrel/export and lint changes. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Caller as Web UI / Tests
participant AI as choose-args / getActionItemsWithAiArgs
participant LLM as getModel / Provider
Caller->>AI: getActionItemsWithAiArgs({ client, modelType, ... })
AI->>LLM: getModel(modelType)
alt provider unknown
LLM-->>AI: throw Error("Unknown provider: ${provider}")
AI-->>Caller: propagate error
else provider found
LLM-->>AI: generate args
AI-->>Caller: return action items
end
sequenceDiagram
autonumber
participant Caller
participant Draft as aiDraftWithKnowledge
participant LLM
Caller->>Draft: requestDraft(email, { emailHistoryContext? })
Draft->>LLM: chatCompletionStream(messages: [{role:"system",...}, {role:"user",...}], ...)
LLM-->>Draft: draft reply
Draft-->>Caller: return reply
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
Status, Documentation and Community
|
|
Claude finished @elie222's task —— View job PR Review: Package UpgradesI've conducted a comprehensive review of this package upgrade PR. Here are my findings: Review Checklist
✅ What Looks GoodSystematic Upgrade Approach:
Security & Configuration:
Development Workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 8
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (6)
packages/tinybird/src/index.ts (1)
5-5: Export missing in delete.ts
The barrelexport * from "./delete";exports nothing becausedelete.tsdefinesdeleteFromDatasourceand_deleteFromDatasourceWithRetrywithout anyexportmodifiers. Add exports, for example:export async function deleteFromDatasource(...) { … } export async function _deleteFromDatasourceWithRetry(...) { … }apps/web/utils/usage.ts (1)
24-41: try/catch is ineffective without awaiting Promise.all.Errors from
publishAiCall/saveUsagewon’t be caught because the promise isn’t awaited. Either await and return the result, or intentionally let rejections bubble (and remove try/catch).try { - return Promise.all([ + const result = await Promise.all([ publishAiCall({ userId: email, provider, model, totalTokens: usage.totalTokens ?? 0, completionTokens: usage.outputTokens ?? 0, promptTokens: usage.inputTokens ?? 0, cost, timestamp: Date.now(), label, }), saveUsage({ email, cost, usage }), ]); + return result; } catch (error) { logger.error("Failed to save usage", { error }); }apps/web/app/(app)/[emailAccountId]/reply-zero/ReplyTrackerEmails.tsx (2)
482-493: Avoid unhandled Promise rejections from onActiononAction now returns a Promise, but handleKeyAction neither awaits nor catches it. Wrap calls to prevent unhandled rejections triggered by key events.
- const handleKeyAction = useCallback( - (index: number, key: string) => { - if (key === "r") onAction(index, "reply"); - else if (key === "d") onAction(index, "resolve"); - else if (key === "n") onAction(index, "unresolve"); - }, + const handleKeyAction = useCallback( + (index: number, key: string) => { + const run = (action: "reply" | "resolve" | "unresolve") => + onAction(index, action).catch(() => { + /* noop – errors are surfaced via toasts in handlers */ + }); + if (key === "r") run("reply"); + else if (key === "d") run("resolve"); + else if (key === "n") run("unresolve"); + },
118-131: Guard against missing last messagethread.messages.at(-1)! can be undefined. Add a defensive check to avoid runtime errors with empty threads.
- const message = thread.messages.at(-1)!; + const message = thread.messages.at(-1); + if (!message) return;apps/web/__tests__/ai-extract-from-email-history.test.ts (1)
66-77: Fix mismatch with implementation: expect null for empty historyaiExtractFromEmailHistory returns null when historicalMessages is empty. The test expects a string.
- expect(result).toBeDefined(); - expect(result).toBe("No relevant historical context available."); + expect(result).toBeNull();apps/web/utils/outlook/client.ts (1)
90-93: Token expiry unit mismatch (seconds vs ms) causes unnecessary refreshesexpires_at is saved in seconds, but compared to Date.now() (ms). Convert before comparing.
- const expiryDate = expiresAt ? expiresAt : null; - if (accessToken && expiryDate && expiryDate > Date.now()) { + const expiryDate = expiresAt ?? null; // seconds + if (accessToken && expiryDate && expiryDate * 1000 > Date.now()) { return createOutlookClient(accessToken); }
🧹 Nitpick comments (29)
packages/tinybird-ai-analytics/package.json (1)
10-13: Add a Node engine constraint at the repo root
Add the following to the workspace-root package.json to lock the Node runtime to match @types/node@24.3.0:"engines": { "node": ">=24.3.0" }This ensures your CI and local runtime align with the types you’re using.
packages/loops/src/index.ts (1)
1-1: Inline lint-disable for barrel files — consider centralizing.Looks good for now; optionally move this rule to biome config overrides for barrel files to avoid per-file comments.
packages/tinybird/src/index.ts (1)
1-1: Prefer targeted lint ignore over file-wide suppressionUse a single-line directive above the export(s) instead of ignore-all to reduce scope.
-/** biome-ignore-all lint/performance/noBarrelFile: fix later */ +/** biome-ignore lint/performance/noBarrelFile: Barrel file intentional */apps/web/__tests__/writing-style.test.ts (1)
21-33: Optional: add debug logs of AI outputs per test rulesAdd console.debug of salient generated fields to aid flakiness triage.
console.debug("writing-style result", { typicalLength: result?.typicalLength, formality: result?.formality, commonGreeting: result?.commonGreeting, notableTraitsCount: result?.notableTraits?.length, examplesCount: result?.examples?.length, });packages/resend/src/index.ts (1)
1-1: Barrel lint suppression acknowledgedIndex remains a purposeful barrel; suppression is fine. Consider narrowing to single-line ignore as in tinybird for consistency.
-/** biome-ignore-all lint/performance/noBarrelFile: fix later */ +/** biome-ignore lint/performance/noBarrelFile: Barrel file intentional */apps/unsubscriber/src/llm.ts (1)
8-20: Make switch exhaustive at compile time (avoid default)Use a never-check helper to catch future provider additions during CI, while still throwing at runtime.
function assertUnreachable(x: never): never { throw new Error(`Unknown provider: ${x as string}`); } export function getModel(provider: LLMProvider) { switch (provider) { case "google": return google("gemini-1.5-flash"); case "openai": return openai("gpt-4o-mini"); case "anthropic": return anthropic("claude-3-7-sonnet-20250219"); case "bedrock": return bedrock("anthropic.claude-3-7-sonnet-20250219-v1:0"); } return assertUnreachable(provider); }apps/web/utils/usage.ts (3)
1-1: Scope the noMagicNumbers suppression more narrowly.File-wide suppression can hide unrelated issues. Consider limiting it to the cost table blocks or replacing with targeted
// biome-ignore lint/style/noMagicNumberscomments.
22-22: Typo: rename calcuateCost → calculateCost.Prevents future confusion and improves searchability.
- const cost = calcuateCost(model, usage); + const cost = calculateCost(model, usage); @@ -function calcuateCost(model: string, usage: LanguageModelUsage): number { +function calculateCost(model: string, usage: LanguageModelUsage): number {Also applies to: 140-140
141-145: Log unknown model rates to catch gaps in the cost table.Returning 0 silently can hide usage costs.
- if (!costs[model]) return 0; + if (!costs[model]) { + logger.warn("Missing cost mapping for model", { model }); + return 0; + }packages/tinybird-ai-analytics/src/index.ts (1)
1-1: Barrel-file lint suppression acknowledged.Ok as a temporary measure. Consider replacing with explicit imports/exports or module-level re-exports later to satisfy the rule.
apps/web/__tests__/ai-detect-recurring-pattern.test.ts (1)
110-110: Avoid randomness in tests for determinism.
Math.random()can cause flaky AI prompts and snapshots. Use a deterministic value derived fromi.- Total: $${(Math.random() * 100).toFixed(2)} + Total: $${(10 + i).toFixed(2)}apps/unsubscriber/package.json (1)
1-32: Consider declaring engines to prevent Node/type mismatches at runtime.Locks CI/prod to versions compatible with TS 5.9, @types/node 24, fastify 5, and dotenv 17.
{ "name": "inbox-zero-unsubscriber", "version": "1.0.0", "private": true, "type": "module", "main": "index.js", + "engines": { + "node": ">=20.11" + }, "scripts": {apps/web/__tests__/ai-extract-knowledge.test.ts (1)
6-7: Centralized TIMEOUT looks goodUsing a per-file TIMEOUT constant improves readability and maintenance. Consider later centralizing common AI test constants in a shared test helper to DRY.
apps/web/__tests__/ai/reply/draft-with-knowledge.test.ts (1)
79-80: EmailForLLM.date is correctly defined as Date—no changes needed.
- Optional: replace TIMEOUT with a dedicated SPACING_MS constant when calculating message spacing for clarity.
apps/web/__tests__/ai-create-group.test.ts (1)
20-71: Deterministic mocking + real LLM path looks good; add a couple of assertions/logs
- Nice: mocking queryBatchMessages while still exercising the real LLM.
- Suggest adding visibility into the generated output and ensuring external senders aren’t included.
const result = await aiGenerateGroupItems(emailAccount, gmail, group); expect(result).toEqual({ senders: expect.arrayContaining(["@mycompany.com"]), subjects: expect.arrayContaining(["Project Update:", "Team Meeting:"]), }); - expect(queryBatchMessages).toHaveBeenCalled(); + // Sanity: AI shouldn’t include unrelated external senders + expect(result.senders.some((s) => s.includes("@external.com"))).toBe(false); + + // Helpful when RUN_AI_TESTS=true to inspect output + console.debug("Generated group items:", result); + + expect(queryBatchMessages).toHaveBeenCalled();apps/web/app/(app)/[emailAccountId]/reply-zero/ReplyTrackerEmails.tsx (3)
139-162: Remove unnecessary async or await somethingonSendSuccess is marked async but doesn’t await anything. Drop async to avoid returning an unused Promise.
- const onSendSuccess = useCallback( - async (_messageId: string, threadId: string) => { + const onSendSuccess = useCallback( + (_messageId: string, threadId: string) => {
150-159: Cleanup timeout on unmount to avoid leaksThe 3-minute setTimeout isn’t cleared on unmount. Track and clear it in a cleanup effect.
- const timeout = 3 * 60 * 1000; - setTimeout(() => { + const timeout = 3 * 60 * 1000; + const id = setTimeout(() => { setRecentlySentThreads((prev) => { const next = new Set(prev); next.delete(threadId); return next; }); - }, timeout); + }, timeout); + return () => clearTimeout(id);Note: If you keep this inside the callback, you can store ids in a ref and clear them in a useEffect cleanup.
382-384: Remove stray Tailwind class “dark”dark is a variant prefix, not a standalone class. Replace with dark:… utility or drop it.
- <div className="dark ml-2"> + <div className="ml-2">apps/web/utils/outlook/client.ts (2)
124-131: Clarify stored expiry to seconds and document intentMake the seconds semantics explicit to avoid future regressions.
- await saveTokens({ + // Store as UNIX seconds + await saveTokens({ tokens: { access_token: tokens.access_token, - expires_at: Math.floor(Date.now() / 1000 + tokens.expires_in), + // tokens.expires_in is seconds + expires_at: Math.floor(Date.now() / 1000 + tokens.expires_in), },Add a comment where expiresAt is passed into getOutlookClientWithRefresh to indicate it should be seconds.
11-16: Remove duplicate/unused option nameAuthOptions lists both expiryDate and expiresAt; only expiresAt is used. Drop the extra to reduce confusion.
type AuthOptions = { accessToken?: string | null; refreshToken?: string | null; - expiryDate?: number | null; expiresAt?: number | null; };apps/web/utils/email/microsoft.ts (1)
808-816: Avoid atob in Node runtime; use Buffer for base64 decodingatob isn’t guaranteed in Node. Prefer Buffer to avoid SSR/runtime errors while preserving the existing try/catch fallback.
- return atob(message.conversationIndex || "").length > 22; + const idx = message.conversationIndex ?? ""; + const bytes = + typeof atob === "function" + ? Uint8Array.from(atob(idx), (c) => c.charCodeAt(0)) + : Buffer.from(idx, "base64"); + return bytes.length > 22;apps/web/__tests__/ai-example-matches.test.ts (1)
69-74: Add debug log for generated AI result (per test guidelines)Helps diagnose flakiness when RUN_AI_TESTS=true.
- const result = await aiFindExampleMatches( + const result = await aiFindExampleMatches( emailAccount, gmail, rulesPrompt, ); + console.debug("aiFindExampleMatches result:", JSON.stringify(result));apps/web/__tests__/ai-categorize-senders.test.ts (1)
54-84: Minor test ergonomics: reduce duplication and speed up failures
- Cache categories in a const to avoid recomputing getEnabledCategories().
- Consider table-driven tests (describe.each) for sender cases to get per-row reporting.
- const result = await aiCategorizeSenders({ + const categories = getEnabledCategories(); + const result = await aiCategorizeSenders({ emailAccount, - senders: testSenders, - categories: getEnabledCategories(), + senders: testSenders, + categories, });Also applies to: 96-122, 126-147, 149-169, 173-221
biome.json (1)
33-37: Consider enabling noTsIgnoreIf feasible, turning on noTsIgnore helps prevent hidden type issues, especially after dependency upgrades.
apps/web/package.json (1)
150-150: Move TypeScript to devDependencies to avoid shipping build tooling in prod.
typescriptis only needed for build/test; keeping it in dependencies increases install size and attack surface.Apply this diff:
@@ - "typescript": "5.9.2", + @@ "devDependencies": { @@ - "vitest": "3.2.4", + "vitest": "3.2.4", "vitest-mock-extended": "3.1.0", + "typescript": "5.9.2" },apps/web/__tests__/ai-choose-args.test.ts (2)
27-29: Avoidas any; use a typed stub without leakingany.For stronger type-safety in tests, derive the client type from the SUT instead of
as any.Apply this diff in each call site:
- client: {} as any, + client: {} as Parameters<typeof getActionItemsWithAiArgs>[0]["client"],Optionally, extract a
getMockClient()helper in__tests__/helpers.tsreturning the same typed stub to reduce repetition.Also applies to: 50-52, 80-82, 110-112, 148-150
84-88: Reduce flakiness in LLM assertion by relaxing exact string match.Exact equality is brittle for real LLM output. Prefer contains + prefix match.
Apply this diff:
- expect(result[0]).toMatchObject({ - ...actions[0], - content: "The price of pears is: $1.99", - }); + expect(result[0].content).toMatch(/^The price of pears is:?/); + expect(result[0].content).toContain("$1.99");apps/web/__tests__/ai-prompt-to-rules.test.ts (2)
36-36: Preferconsole.debugin AI tests.Project rules allow
console.debugfor AI outputs; switch fromconsole.log.Apply this diff:
- console.log(JSON.stringify(result, null, 2)); + console.debug(JSON.stringify(result, null, 2));
319-321: Normalize how reply content is asserted (contentvsfields.content).Tests mix
action.contentandaction.fields.content. Standardize with a fallback to avoid coupling to internal shape.Apply this diff:
- { - type: ActionType.REPLY, - content: expect.stringMatching(/Hi {{firstName}}/), - }, + { + type: ActionType.REPLY, + },And replace the follow-up assertion with:
- expect(replyAction?.fields?.content).toContain("{{firstName}}"); + const content = (replyAction as any)?.fields?.content ?? (replyAction as any)?.content; + expect(content).toContain("{{firstName}}");Also applies to: 325-330
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (35)
.cursor/rules/llm-test.mdc(3 hunks)apps/mcp-server/package.json(1 hunks)apps/unsubscriber/package.json(2 hunks)apps/unsubscriber/src/llm.ts(1 hunks)apps/web/__tests__/ai-categorize-senders.test.ts(3 hunks)apps/web/__tests__/ai-choose-args.test.ts(8 hunks)apps/web/__tests__/ai-create-group.test.ts(1 hunks)apps/web/__tests__/ai-detect-recurring-pattern.test.ts(3 hunks)apps/web/__tests__/ai-diff-rules.test.ts(1 hunks)apps/web/__tests__/ai-example-matches.test.ts(2 hunks)apps/web/__tests__/ai-extract-from-email-history.test.ts(4 hunks)apps/web/__tests__/ai-extract-knowledge.test.ts(3 hunks)apps/web/__tests__/ai-prompt-to-rules.test.ts(4 hunks)apps/web/__tests__/ai/reply/draft-with-knowledge.test.ts(4 hunks)apps/web/__tests__/writing-style.test.ts(2 hunks)apps/web/app/(app)/[emailAccountId]/reply-zero/ReplyTrackerEmails.tsx(1 hunks)apps/web/app/(app)/[emailAccountId]/reply-zero/date-filter.ts(1 hunks)apps/web/app/(marketing)(1 hunks)apps/web/package.json(3 hunks)apps/web/utils/ai/digest/summarize-email-for-digest.test.ts(2 hunks)apps/web/utils/braintrust.ts(1 hunks)apps/web/utils/email/google.ts(1 hunks)apps/web/utils/email/microsoft.ts(1 hunks)apps/web/utils/outlook/client.ts(1 hunks)apps/web/utils/usage.ts(1 hunks)biome.json(5 hunks)package.json(2 hunks)packages/loops/package.json(1 hunks)packages/loops/src/index.ts(1 hunks)packages/resend/package.json(1 hunks)packages/resend/src/index.ts(1 hunks)packages/tinybird-ai-analytics/package.json(1 hunks)packages/tinybird-ai-analytics/src/index.ts(1 hunks)packages/tinybird/package.json(1 hunks)packages/tinybird/src/index.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (27)
apps/web/app/**
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
NextJS app router structure with (app) directory
Files:
apps/web/app/(marketing)apps/web/app/(app)/[emailAccountId]/reply-zero/ReplyTrackerEmails.tsxapps/web/app/(app)/[emailAccountId]/reply-zero/date-filter.ts
!{.cursor/rules/*.mdc}
📄 CodeRabbit inference engine (.cursor/rules/cursor-rules.mdc)
Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location
Files:
apps/web/app/(marketing)apps/unsubscriber/src/llm.tspackage.jsonapps/web/__tests__/writing-style.test.tsapps/web/utils/usage.tspackages/resend/src/index.tspackages/loops/src/index.tspackages/tinybird/package.jsonapps/mcp-server/package.jsonpackages/tinybird-ai-analytics/src/index.tsapps/web/__tests__/ai-create-group.test.tsapps/web/utils/email/google.tsapps/web/__tests__/ai-extract-knowledge.test.tsapps/unsubscriber/package.json.cursor/rules/llm-test.mdcapps/web/utils/outlook/client.tspackages/tinybird/src/index.tsapps/web/__tests__/ai-diff-rules.test.tspackages/resend/package.jsonapps/web/utils/email/microsoft.tspackages/tinybird-ai-analytics/package.jsonapps/web/utils/braintrust.tsapps/web/__tests__/ai-extract-from-email-history.test.tspackages/loops/package.jsonapps/web/__tests__/ai/reply/draft-with-knowledge.test.tsapps/web/app/(app)/[emailAccountId]/reply-zero/ReplyTrackerEmails.tsxapps/web/__tests__/ai-categorize-senders.test.tsapps/web/__tests__/ai-example-matches.test.tsapps/web/__tests__/ai-detect-recurring-pattern.test.tsapps/web/app/(app)/[emailAccountId]/reply-zero/date-filter.tsapps/web/utils/ai/digest/summarize-email-for-digest.test.tsbiome.jsonapps/web/package.jsonapps/web/__tests__/ai-prompt-to-rules.test.tsapps/web/__tests__/ai-choose-args.test.ts
!pages/_document.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.
Files:
apps/web/app/(marketing)apps/unsubscriber/src/llm.tspackage.jsonapps/web/__tests__/writing-style.test.tsapps/web/utils/usage.tspackages/resend/src/index.tspackages/loops/src/index.tspackages/tinybird/package.jsonapps/mcp-server/package.jsonpackages/tinybird-ai-analytics/src/index.tsapps/web/__tests__/ai-create-group.test.tsapps/web/utils/email/google.tsapps/web/__tests__/ai-extract-knowledge.test.tsapps/unsubscriber/package.json.cursor/rules/llm-test.mdcapps/web/utils/outlook/client.tspackages/tinybird/src/index.tsapps/web/__tests__/ai-diff-rules.test.tspackages/resend/package.jsonapps/web/utils/email/microsoft.tspackages/tinybird-ai-analytics/package.jsonapps/web/utils/braintrust.tsapps/web/__tests__/ai-extract-from-email-history.test.tspackages/loops/package.jsonapps/web/__tests__/ai/reply/draft-with-knowledge.test.tsapps/web/app/(app)/[emailAccountId]/reply-zero/ReplyTrackerEmails.tsxapps/web/__tests__/ai-categorize-senders.test.tsapps/web/__tests__/ai-example-matches.test.tsapps/web/__tests__/ai-detect-recurring-pattern.test.tsapps/web/app/(app)/[emailAccountId]/reply-zero/date-filter.tsapps/web/utils/ai/digest/summarize-email-for-digest.test.tsbiome.jsonapps/web/package.jsonapps/web/__tests__/ai-prompt-to-rules.test.tsapps/web/__tests__/ai-choose-args.test.ts
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)
**/*.ts: The same validation should be done in the server action too
Define validation schemas using Zod
Files:
apps/unsubscriber/src/llm.tsapps/web/__tests__/writing-style.test.tsapps/web/utils/usage.tspackages/resend/src/index.tspackages/loops/src/index.tspackages/tinybird-ai-analytics/src/index.tsapps/web/__tests__/ai-create-group.test.tsapps/web/utils/email/google.tsapps/web/__tests__/ai-extract-knowledge.test.tsapps/web/utils/outlook/client.tspackages/tinybird/src/index.tsapps/web/__tests__/ai-diff-rules.test.tsapps/web/utils/email/microsoft.tsapps/web/utils/braintrust.tsapps/web/__tests__/ai-extract-from-email-history.test.tsapps/web/__tests__/ai/reply/draft-with-knowledge.test.tsapps/web/__tests__/ai-categorize-senders.test.tsapps/web/__tests__/ai-example-matches.test.tsapps/web/__tests__/ai-detect-recurring-pattern.test.tsapps/web/app/(app)/[emailAccountId]/reply-zero/date-filter.tsapps/web/utils/ai/digest/summarize-email-for-digest.test.tsapps/web/__tests__/ai-prompt-to-rules.test.tsapps/web/__tests__/ai-choose-args.test.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
**/*.{ts,tsx}: UsecreateScopedLoggerfor logging in backend TypeScript files
Typically add the logger initialization at the top of the file when usingcreateScopedLogger
Only use.with()on a logger instance within a specific function, not for a global loggerImport Prisma in the project using
import prisma from "@/utils/prisma";
**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
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 parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.
Files:
apps/unsubscriber/src/llm.tsapps/web/__tests__/writing-style.test.tsapps/web/utils/usage.tspackages/resend/src/index.tspackages/loops/src/index.tspackages/tinybird-ai-analytics/src/index.tsapps/web/__tests__/ai-create-group.test.tsapps/web/utils/email/google.tsapps/web/__tests__/ai-extract-knowledge.test.tsapps/web/utils/outlook/client.tspackages/tinybird/src/index.tsapps/web/__tests__/ai-diff-rules.test.tsapps/web/utils/email/microsoft.tsapps/web/utils/braintrust.tsapps/web/__tests__/ai-extract-from-email-history.test.tsapps/web/__tests__/ai/reply/draft-with-knowledge.test.tsapps/web/app/(app)/[emailAccountId]/reply-zero/ReplyTrackerEmails.tsxapps/web/__tests__/ai-categorize-senders.test.tsapps/web/__tests__/ai-example-matches.test.tsapps/web/__tests__/ai-detect-recurring-pattern.test.tsapps/web/app/(app)/[emailAccountId]/reply-zero/date-filter.tsapps/web/utils/ai/digest/summarize-email-for-digest.test.tsapps/web/__tests__/ai-prompt-to-rules.test.tsapps/web/__tests__/ai-choose-args.test.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useelements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...
Files:
apps/unsubscriber/src/llm.tsapps/web/__tests__/writing-style.test.tsapps/web/utils/usage.tspackages/resend/src/index.tspackages/loops/src/index.tspackages/tinybird-ai-analytics/src/index.tsapps/web/__tests__/ai-create-group.test.tsapps/web/utils/email/google.tsapps/web/__tests__/ai-extract-knowledge.test.tsapps/web/utils/outlook/client.tspackages/tinybird/src/index.tsapps/web/__tests__/ai-diff-rules.test.tsapps/web/utils/email/microsoft.tsapps/web/utils/braintrust.tsapps/web/__tests__/ai-extract-from-email-history.test.tsapps/web/__tests__/ai/reply/draft-with-knowledge.test.tsapps/web/app/(app)/[emailAccountId]/reply-zero/ReplyTrackerEmails.tsxapps/web/__tests__/ai-categorize-senders.test.tsapps/web/__tests__/ai-example-matches.test.tsapps/web/__tests__/ai-detect-recurring-pattern.test.tsapps/web/app/(app)/[emailAccountId]/reply-zero/date-filter.tsapps/web/utils/ai/digest/summarize-email-for-digest.test.tsapps/web/__tests__/ai-prompt-to-rules.test.tsapps/web/__tests__/ai-choose-args.test.ts
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use@/for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX
Files:
apps/web/__tests__/writing-style.test.tsapps/web/utils/usage.tsapps/web/__tests__/ai-create-group.test.tsapps/web/utils/email/google.tsapps/web/__tests__/ai-extract-knowledge.test.tsapps/web/utils/outlook/client.tsapps/web/__tests__/ai-diff-rules.test.tsapps/web/utils/email/microsoft.tsapps/web/utils/braintrust.tsapps/web/__tests__/ai-extract-from-email-history.test.tsapps/web/__tests__/ai/reply/draft-with-knowledge.test.tsapps/web/app/(app)/[emailAccountId]/reply-zero/ReplyTrackerEmails.tsxapps/web/__tests__/ai-categorize-senders.test.tsapps/web/__tests__/ai-example-matches.test.tsapps/web/__tests__/ai-detect-recurring-pattern.test.tsapps/web/app/(app)/[emailAccountId]/reply-zero/date-filter.tsapps/web/utils/ai/digest/summarize-email-for-digest.test.tsapps/web/__tests__/ai-prompt-to-rules.test.tsapps/web/__tests__/ai-choose-args.test.ts
apps/web/__tests__/**/*
📄 CodeRabbit inference engine (.cursor/rules/llm-test.mdc)
Place all LLM-related tests in apps/web/tests/
Files:
apps/web/__tests__/writing-style.test.tsapps/web/__tests__/ai-create-group.test.tsapps/web/__tests__/ai-extract-knowledge.test.tsapps/web/__tests__/ai-diff-rules.test.tsapps/web/__tests__/ai-extract-from-email-history.test.tsapps/web/__tests__/ai/reply/draft-with-knowledge.test.tsapps/web/__tests__/ai-categorize-senders.test.tsapps/web/__tests__/ai-example-matches.test.tsapps/web/__tests__/ai-detect-recurring-pattern.test.tsapps/web/__tests__/ai-prompt-to-rules.test.tsapps/web/__tests__/ai-choose-args.test.ts
apps/web/__tests__/**/*.test.ts
📄 CodeRabbit inference engine (.cursor/rules/llm-test.mdc)
apps/web/__tests__/**/*.test.ts: Always create helper functions for common test data in LLM-related tests
Include standard test cases: happy path, error handling, edge cases (empty input, null values), different user configurations, and various input formats in LLM-related tests
Set appropriate timeouts for LLM calls in tests (e.g., 15,000ms for long-running operations)
Use descriptive console.debug statements for generated content in LLM-related tests
Do not mock the LLM call in LLM-related tests; call the actual LLM
Test both AI and non-AI paths in LLM-related tests
Files:
apps/web/__tests__/writing-style.test.tsapps/web/__tests__/ai-create-group.test.tsapps/web/__tests__/ai-extract-knowledge.test.tsapps/web/__tests__/ai-diff-rules.test.tsapps/web/__tests__/ai-extract-from-email-history.test.tsapps/web/__tests__/ai/reply/draft-with-knowledge.test.tsapps/web/__tests__/ai-categorize-senders.test.tsapps/web/__tests__/ai-example-matches.test.tsapps/web/__tests__/ai-detect-recurring-pattern.test.tsapps/web/__tests__/ai-prompt-to-rules.test.tsapps/web/__tests__/ai-choose-args.test.ts
**/*.test.{ts,js}
📄 CodeRabbit inference engine (.cursor/rules/security.mdc)
Include security tests in your test suites to verify authentication, authorization, and error handling.
Files:
apps/web/__tests__/writing-style.test.tsapps/web/__tests__/ai-create-group.test.tsapps/web/__tests__/ai-extract-knowledge.test.tsapps/web/__tests__/ai-diff-rules.test.tsapps/web/__tests__/ai-extract-from-email-history.test.tsapps/web/__tests__/ai/reply/draft-with-knowledge.test.tsapps/web/__tests__/ai-categorize-senders.test.tsapps/web/__tests__/ai-example-matches.test.tsapps/web/__tests__/ai-detect-recurring-pattern.test.tsapps/web/utils/ai/digest/summarize-email-for-digest.test.tsapps/web/__tests__/ai-prompt-to-rules.test.tsapps/web/__tests__/ai-choose-args.test.ts
**/*.test.{ts,js,tsx,jsx}
📄 CodeRabbit inference engine (.cursor/rules/testing.mdc)
**/*.test.{ts,js,tsx,jsx}: Tests are colocated next to the tested file (e.g.,dir/format.tsanddir/format.test.ts)
Usevi.mock("server-only", () => ({}));to mock theserver-onlymodule in tests
Mock@/utils/prismain tests usingvi.mock("@/utils/prisma")and use the provided prisma mock
Mock external dependencies in tests
Clean up mocks between tests
Do not mock the Logger
Files:
apps/web/__tests__/writing-style.test.tsapps/web/__tests__/ai-create-group.test.tsapps/web/__tests__/ai-extract-knowledge.test.tsapps/web/__tests__/ai-diff-rules.test.tsapps/web/__tests__/ai-extract-from-email-history.test.tsapps/web/__tests__/ai/reply/draft-with-knowledge.test.tsapps/web/__tests__/ai-categorize-senders.test.tsapps/web/__tests__/ai-example-matches.test.tsapps/web/__tests__/ai-detect-recurring-pattern.test.tsapps/web/utils/ai/digest/summarize-email-for-digest.test.tsapps/web/__tests__/ai-prompt-to-rules.test.tsapps/web/__tests__/ai-choose-args.test.ts
**/__tests__/**/*.{ts,js,tsx,jsx}
📄 CodeRabbit inference engine (.cursor/rules/testing.mdc)
AI tests are placed in the
__tests__directory and are not run by default (they use a real LLM)
Files:
apps/web/__tests__/writing-style.test.tsapps/web/__tests__/ai-create-group.test.tsapps/web/__tests__/ai-extract-knowledge.test.tsapps/web/__tests__/ai-diff-rules.test.tsapps/web/__tests__/ai-extract-from-email-history.test.tsapps/web/__tests__/ai/reply/draft-with-knowledge.test.tsapps/web/__tests__/ai-categorize-senders.test.tsapps/web/__tests__/ai-example-matches.test.tsapps/web/__tests__/ai-detect-recurring-pattern.test.tsapps/web/__tests__/ai-prompt-to-rules.test.tsapps/web/__tests__/ai-choose-args.test.ts
**/*.{test,spec}.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{test,spec}.{js,jsx,ts,tsx}: Don't use export or module.exports in test files.
Don't use focused tests.
Don't use disabled tests.
Make sure the assertion function, like expect, is placed inside an it() function call.
Don't nest describe() blocks too deeply in test files.
Don't use focused tests.
Don't use disabled tests.
Don't use export or module.exports in test files.
Files:
apps/web/__tests__/writing-style.test.tsapps/web/__tests__/ai-create-group.test.tsapps/web/__tests__/ai-extract-knowledge.test.tsapps/web/__tests__/ai-diff-rules.test.tsapps/web/__tests__/ai-extract-from-email-history.test.tsapps/web/__tests__/ai/reply/draft-with-knowledge.test.tsapps/web/__tests__/ai-categorize-senders.test.tsapps/web/__tests__/ai-example-matches.test.tsapps/web/__tests__/ai-detect-recurring-pattern.test.tsapps/web/utils/ai/digest/summarize-email-for-digest.test.tsapps/web/__tests__/ai-prompt-to-rules.test.tsapps/web/__tests__/ai-choose-args.test.ts
apps/web/__tests__/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/llm.mdc)
Place LLM-specific tests under apps/web/tests
Files:
apps/web/__tests__/writing-style.test.tsapps/web/__tests__/ai-create-group.test.tsapps/web/__tests__/ai-extract-knowledge.test.tsapps/web/__tests__/ai-diff-rules.test.tsapps/web/__tests__/ai-extract-from-email-history.test.tsapps/web/__tests__/ai/reply/draft-with-knowledge.test.tsapps/web/__tests__/ai-categorize-senders.test.tsapps/web/__tests__/ai-example-matches.test.tsapps/web/__tests__/ai-detect-recurring-pattern.test.tsapps/web/__tests__/ai-prompt-to-rules.test.tsapps/web/__tests__/ai-choose-args.test.ts
apps/web/utils/**
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Create utility functions in
utils/folder for reusable logic
Files:
apps/web/utils/usage.tsapps/web/utils/email/google.tsapps/web/utils/outlook/client.tsapps/web/utils/email/microsoft.tsapps/web/utils/braintrust.tsapps/web/utils/ai/digest/summarize-email-for-digest.test.ts
apps/web/utils/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
apps/web/utils/**/*.ts: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size
Files:
apps/web/utils/usage.tsapps/web/utils/email/google.tsapps/web/utils/outlook/client.tsapps/web/utils/email/microsoft.tsapps/web/utils/braintrust.tsapps/web/utils/ai/digest/summarize-email-for-digest.test.ts
apps/web/utils/usage.ts
📄 CodeRabbit inference engine (.cursor/rules/llm.mdc)
Implement usage tracking and monitoring in apps/web/utils/usage.ts
Files:
apps/web/utils/usage.ts
.cursor/rules/*.mdc
📄 CodeRabbit inference engine (.cursor/rules/cursor-rules.mdc)
.cursor/rules/*.mdc: Always place rule files in PROJECT_ROOT/.cursor/rules/
Follow the naming convention: use kebab-case for filenames, always use .mdc extension, and make names descriptive of the rule's purpose
Cursor rules must have a frontmatter section with description, globs, and alwaysApply fields, followed by markdown content explaining the rule
Files:
.cursor/rules/llm-test.mdc
apps/web/**/*.tsx
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.tsx: Follow tailwindcss patterns with prettier-plugin-tailwindcss
Prefer functional components with hooks
Use shadcn/ui components when available
Ensure responsive design with mobile-first approach
Follow consistent naming conventions (PascalCase for components)
Use LoadingContent component for async data
Useresult?.serverErrorwithtoastErrorandtoastSuccess
UseLoadingContentcomponent to handle loading and error states consistently
Passloading,error, and children props toLoadingContent
Files:
apps/web/app/(app)/[emailAccountId]/reply-zero/ReplyTrackerEmails.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)
**/*.tsx: Use React Hook Form with Zod for validation
Validate form inputs before submission
Show validation errors inline next to form fields
Files:
apps/web/app/(app)/[emailAccountId]/reply-zero/ReplyTrackerEmails.tsx
apps/web/app/(app)/*/**
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
Components for the page are either put in page.tsx, or in the apps/web/app/(app)/PAGE_NAME folder
Files:
apps/web/app/(app)/[emailAccountId]/reply-zero/ReplyTrackerEmails.tsxapps/web/app/(app)/[emailAccountId]/reply-zero/date-filter.ts
apps/web/app/(app)/*/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
If you need to use onClick in a component, that component is a client component and file must start with 'use client'
Files:
apps/web/app/(app)/[emailAccountId]/reply-zero/ReplyTrackerEmails.tsx
apps/web/app/(app)/*/**/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/page-structure.mdc)
If we're in a deeply nested component we will use swr to fetch via API
Files:
apps/web/app/(app)/[emailAccountId]/reply-zero/ReplyTrackerEmails.tsx
apps/web/app/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Components with
onClickmust be client components withuse clientdirective
Files:
apps/web/app/(app)/[emailAccountId]/reply-zero/ReplyTrackerEmails.tsx
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{jsx,tsx}: Don't destructure props inside JSX components in Solid projects.
Don't use both children and dangerouslySetInnerHTML props on the same element.
Don't use Array index in keys.
Don't assign to React component props.
Don't define React components inside other components.
Don't use event handlers on non-interactive elements.
Don't assign JSX properties multiple times.
Don't add extra closing tags for components without children.
Use <>...</> instead of ....
Don't insert comments as text nodes.
Don't use the return value of React.render.
Make sure all dependencies are correctly specified in React hooks.
Make sure all React hooks are called from the top level of component functions.
Don't use unnecessary fragments.
Don't pass children as props.
Use semantic elements instead of role attributes in JSX.
Files:
apps/web/app/(app)/[emailAccountId]/reply-zero/ReplyTrackerEmails.tsx
**/*.{html,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{html,jsx,tsx}: Don't use or elements.
Don't use accessKey attribute on any HTML element.
Don't set aria-hidden="true" on focusable elements.
Don't add ARIA roles, states, and properties to elements that don't support them.
Only use the scope prop on elements.
Don't assign non-interactive ARIA roles to interactive HTML elements.
Make sure label elements have text content and are associated with an input.
Don't assign interactive ARIA roles to non-interactive HTML elements.
Don't assign tabIndex to non-interactive HTML elements.
Don't use positive integers for tabIndex property.
Don't include "image", "picture", or "photo" in img alt prop.
Don't use explicit role property that's the same as the implicit/default role.
Make static elements with click handlers use a valid role attribute.
Always include a title element for SVG elements.
Give all elements requiring alt text meaningful information for screen readers.
Make sure anchors have content that's accessible to screen readers.
Assign tabIndex to non-interactive HTML elements with aria-activedescendant.
Include all required ARIA attributes for elements with ARIA roles.
Make sure ARIA properties are valid for the element's supported roles.
Always include a type attribute for button elements.
Make elements with interactive roles and handlers focusable.
Give heading elements content that's accessible to screen readers (not hidden with aria-hidden).
Always include a lang attribute on the html element.
Always include a title attribute for iframe elements.
Accompany onClick with at least one of: onKeyUp, onKeyDown, or onKeyPress.
Accompany onMouseOver/onMouseOut with onFocus/onBlur.
Include caption tracks for audio and video elements.
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 ARIA state and property values.
Use valid values for the autocomplete attribute on input eleme...Files:
apps/web/app/(app)/[emailAccountId]/reply-zero/ReplyTrackerEmails.tsxapps/web/utils/{ai,llms}/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/llm.mdc)
apps/web/utils/{ai,llms}/**/*.ts: Place LLM-related implementation code under apps/web/utils/ai or apps/web/utils/llms
Keep system and user prompts separate; system defines role/task, user contains data/context
Always validate LLM responses with a specific Zod schema
Use descriptive scoped loggers per feature and log inputs/outputs with appropriate levels and context
Implement early returns for invalid inputs and use proper error types with logging
Add fallbacks for AI failures and include retry logic for transient errors using withRetry
Format prompts with XML-like tags; remove excessive whitespace; truncate overly long inputs; keep formatting consistent
Use TypeScript types for all parameters/returns and define interfaces for complex IO structures
Keep related AI functions co-located; extract shared logic into utilities; document complex AI logic with clear comments
Call LLMs via createGenerateObject; pass system, prompt, and a Zod schema; return the validated result.object
Derive model options using getModel(...) and pass them to createGenerateObject and the generate callFiles:
apps/web/utils/ai/digest/summarize-email-for-digest.test.ts🧠 Learnings (20)
📚 Learning: 2025-08-17T16:57:25.834Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/llm.mdc:0-0 Timestamp: 2025-08-17T16:57:25.834Z Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Place LLM-related implementation code under apps/web/utils/ai or apps/web/utils/llmsApplied to files:
apps/unsubscriber/src/llm.ts📚 Learning: 2025-07-18T15:06:10.570Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/llm-test.mdc:0-0 Timestamp: 2025-07-18T15:06:10.570Z Learning: Applies to apps/web/__tests__/**/*.test.ts : Set appropriate timeouts for LLM calls in tests (e.g., 15,000ms for long-running operations)Applied to files:
apps/web/__tests__/writing-style.test.tsapps/web/__tests__/ai-create-group.test.tsapps/web/__tests__/ai-extract-knowledge.test.ts.cursor/rules/llm-test.mdcapps/web/__tests__/ai-diff-rules.test.tsapps/web/__tests__/ai-extract-from-email-history.test.tsapps/web/__tests__/ai/reply/draft-with-knowledge.test.tsapps/web/__tests__/ai-categorize-senders.test.tsapps/web/__tests__/ai-example-matches.test.tsapps/web/__tests__/ai-detect-recurring-pattern.test.tsapps/web/utils/ai/digest/summarize-email-for-digest.test.tsapps/web/__tests__/ai-prompt-to-rules.test.tsapps/web/__tests__/ai-choose-args.test.ts📚 Learning: 2025-07-18T15:06:10.570Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/llm-test.mdc:0-0 Timestamp: 2025-07-18T15:06:10.570Z Learning: Run AI tests with: pnpm test-ai your-featureApplied to files:
apps/web/__tests__/writing-style.test.tsapps/web/__tests__/ai-extract-from-email-history.test.tsapps/web/__tests__/ai-example-matches.test.ts📚 Learning: 2025-07-18T15:06:10.570Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/llm-test.mdc:0-0 Timestamp: 2025-07-18T15:06:10.570Z Learning: Applies to apps/web/__tests__/**/*.test.ts : Test both AI and non-AI paths in LLM-related testsApplied to files:
apps/web/__tests__/writing-style.test.tsapps/web/__tests__/ai-create-group.test.tsapps/web/__tests__/ai-extract-knowledge.test.ts.cursor/rules/llm-test.mdcapps/web/__tests__/ai-diff-rules.test.tsapps/web/__tests__/ai-extract-from-email-history.test.tsapps/web/__tests__/ai/reply/draft-with-knowledge.test.tsapps/web/__tests__/ai-categorize-senders.test.tsapps/web/__tests__/ai-example-matches.test.tsapps/web/__tests__/ai-detect-recurring-pattern.test.tsapps/web/__tests__/ai-prompt-to-rules.test.tsapps/web/__tests__/ai-choose-args.test.ts📚 Learning: 2025-08-17T16:57:25.834Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/llm.mdc:0-0 Timestamp: 2025-08-17T16:57:25.834Z Learning: Applies to apps/web/utils/usage.ts : Implement usage tracking and monitoring in apps/web/utils/usage.tsApplied to files:
apps/web/utils/usage.ts📚 Learning: 2025-08-17T16:57:25.834Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/llm.mdc:0-0 Timestamp: 2025-08-17T16:57:25.834Z Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Keep related AI functions co-located; extract shared logic into utilities; document complex AI logic with clear commentsApplied to files:
apps/web/utils/usage.tsapps/web/__tests__/ai-create-group.test.tsapps/web/__tests__/ai-extract-knowledge.test.tsapps/web/__tests__/ai-diff-rules.test.tsapps/web/__tests__/ai-extract-from-email-history.test.tsapps/web/__tests__/ai-example-matches.test.tsapps/web/__tests__/ai-detect-recurring-pattern.test.tsapps/web/__tests__/ai-prompt-to-rules.test.tsapps/web/__tests__/ai-choose-args.test.ts📚 Learning: 2025-07-20T09:03:06.318Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/ultracite.mdc:0-0 Timestamp: 2025-07-20T09:03:06.318Z Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't export empty modules that don't change anything.Applied to files:
packages/resend/src/index.ts📚 Learning: 2025-07-20T09:03:06.318Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/ultracite.mdc:0-0 Timestamp: 2025-07-20T09:03:06.318Z Learning: Applies to **/*.{js,jsx,ts,tsx} : Use while loops instead of for loops when you don't need initializer and update expressions.Applied to files:
packages/loops/src/index.ts📚 Learning: 2025-07-18T15:05:34.899Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/gmail-api.mdc:0-0 Timestamp: 2025-07-18T15:05:34.899Z Learning: Applies to apps/web/utils/gmail/**/*.ts : Keep provider-specific implementation details isolated in the appropriate utils subfolder (e.g., 'apps/web/utils/gmail/')Applied to files:
apps/web/utils/email/google.ts📚 Learning: 2025-08-17T16:57:25.834Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/llm.mdc:0-0 Timestamp: 2025-08-17T16:57:25.834Z Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Add fallbacks for AI failures and include retry logic for transient errors using withRetryApplied to files:
apps/web/__tests__/ai-extract-knowledge.test.tsapps/web/__tests__/ai-example-matches.test.tsapps/web/__tests__/ai-detect-recurring-pattern.test.tsapps/web/__tests__/ai-prompt-to-rules.test.tsapps/web/__tests__/ai-choose-args.test.ts📚 Learning: 2025-06-23T12:27:30.570Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/testing.mdc:0-0 Timestamp: 2025-06-23T12:27:30.570Z Learning: When mocking Prisma in Vitest, import the Prisma mock from '@/utils/__mocks__/prisma', mock '@/utils/prisma', and clear all mocks in a beforeEach hook to ensure test isolation.Applied to files:
apps/web/__tests__/ai-extract-knowledge.test.ts📚 Learning: 2025-07-18T15:06:10.570Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/llm-test.mdc:0-0 Timestamp: 2025-07-18T15:06:10.570Z Learning: Applies to apps/web/__tests__/**/*.test.ts : Use descriptive console.debug statements for generated content in LLM-related testsApplied to files:
.cursor/rules/llm-test.mdc📚 Learning: 2025-07-18T15:06:10.570Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/llm-test.mdc:0-0 Timestamp: 2025-07-18T15:06:10.570Z Learning: Applies to apps/web/__tests__/**/*.test.ts : Do not mock the LLM call in LLM-related tests; call the actual LLMApplied to files:
.cursor/rules/llm-test.mdc📚 Learning: 2025-07-18T15:06:10.570Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/llm-test.mdc:0-0 Timestamp: 2025-07-18T15:06:10.570Z Learning: Applies to apps/web/__tests__/**/*.test.ts : Always create helper functions for common test data in LLM-related testsApplied to files:
.cursor/rules/llm-test.mdcapps/web/__tests__/ai/reply/draft-with-knowledge.test.tsapps/web/__tests__/ai-detect-recurring-pattern.test.ts📚 Learning: 2025-07-18T15:06:10.570Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/llm-test.mdc:0-0 Timestamp: 2025-07-18T15:06:10.570Z Learning: Applies to apps/web/__tests__/**/*.test.ts : Include standard test cases: happy path, error handling, edge cases (empty input, null values), different user configurations, and various input formats in LLM-related testsApplied to files:
.cursor/rules/llm-test.mdcapps/web/__tests__/ai-detect-recurring-pattern.test.ts📚 Learning: 2025-07-20T09:03:06.318Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/ultracite.mdc:0-0 Timestamp: 2025-07-20T09:03:06.318Z Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't use empty block statements and static blocks.Applied to files:
biome.json📚 Learning: 2025-07-20T09:03:06.318Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/ultracite.mdc:0-0 Timestamp: 2025-07-20T09:03:06.318Z Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't use constant expressions in conditions.Applied to files:
biome.json📚 Learning: 2025-07-20T09:03:06.318Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/ultracite.mdc:0-0 Timestamp: 2025-07-20T09:03:06.318Z Learning: Applies to **/*.{js,jsx,ts,tsx} : Don't have redundant "use strict".Applied to files:
biome.json📚 Learning: 2025-08-17T16:57:25.834Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/llm.mdc:0-0 Timestamp: 2025-08-17T16:57:25.834Z Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Format prompts with XML-like tags; remove excessive whitespace; truncate overly long inputs; keep formatting consistentApplied to files:
apps/web/__tests__/ai-prompt-to-rules.test.ts📚 Learning: 2025-07-18T17:27:58.249Z
Learnt from: CR PR: elie222/inbox-zero#0 File: .cursor/rules/server-actions.mdc:0-0 Timestamp: 2025-07-18T17:27:58.249Z Learning: Applies to apps/web/utils/actions/*.ts : Use `actionClient` when both authenticated user context and a specific `emailAccountId` are needed. The `emailAccountId` must be bound when calling the action from the client.Applied to files:
apps/web/__tests__/ai-choose-args.test.ts🧬 Code graph analysis (10)
apps/web/__tests__/ai-create-group.test.ts (4)
apps/web/__tests__/helpers.ts (1)
getEmailAccount(4-18)apps/web/utils/gmail/message.ts (1)
queryBatchMessages(272-304)apps/web/utils/types.ts (1)
ParsedMessage(55-70)apps/web/utils/ai/group/create-group.ts (1)
aiGenerateGroupItems(56-124)apps/web/__tests__/ai-extract-knowledge.test.ts (2)
apps/web/utils/ai/knowledge/extract.ts (1)
aiExtractRelevantKnowledge(85-119)apps/web/__tests__/helpers.ts (1)
getEmailAccount(4-18)apps/web/__tests__/ai-diff-rules.test.ts (2)
apps/web/__tests__/helpers.ts (1)
getEmailAccount(4-18)apps/web/utils/ai/rule/diff-rules.ts (1)
aiDiffRules(7-90)apps/web/utils/email/microsoft.ts (1)
apps/web/utils/outlook/client.ts (1)
OutlookClient(19-63)apps/web/__tests__/ai-extract-from-email-history.test.ts (2)
apps/web/__tests__/helpers.ts (1)
getEmailAccount(4-18)apps/web/utils/ai/knowledge/extract-from-email-history.ts (1)
aiExtractFromEmailHistory(76-119)apps/web/__tests__/ai-categorize-senders.test.ts (2)
apps/web/utils/ai/categorize-sender/ai-categorize-senders.ts (2)
aiCategorizeSenders(23-122)REQUEST_MORE_INFORMATION_CATEGORY(10-10)apps/web/utils/ai/categorize-sender/ai-categorize-single-sender.ts (1)
aiCategorizeSender(8-71)apps/web/__tests__/ai-example-matches.test.ts (4)
apps/web/__tests__/helpers.ts (1)
getEmailAccount(4-18)apps/web/utils/gmail/message.ts (1)
queryBatchMessages(272-304)apps/web/utils/types.ts (1)
ParsedMessage(55-70)apps/web/utils/ai/example-matches/find-example-matches.ts (2)
aiFindExampleMatches(30-136)findExampleMatchesSchema(11-28)apps/web/utils/ai/digest/summarize-email-for-digest.test.ts (2)
apps/web/utils/ai/digest/summarize-email-for-digest.ts (1)
aiSummarizeEmailForDigest(17-83)apps/web/app/api/resend/digest/validation.ts (1)
DigestEmailSummarySchema(4-4)apps/web/__tests__/ai-prompt-to-rules.test.ts (3)
apps/web/__tests__/helpers.ts (1)
getEmailAccount(4-18)apps/web/utils/ai/rule/prompt-to-rules.ts (1)
aiPromptToRules(15-70)apps/web/utils/ai/rule/create-rule-schema.ts (1)
createRuleSchema(100-107)apps/web/__tests__/ai-choose-args.test.ts (2)
apps/web/utils/ai/choose-rule/choose-args.ts (1)
getActionItemsWithAiArgs(26-92)apps/web/__tests__/helpers.ts (1)
getEmailAccount(4-18)⏰ 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: Jit Security
- GitHub Check: Socket Security: Pull Request Alerts
- GitHub Check: claude-review
| "@types/node": "24.3.0", | ||
| "playwright": "1.55.0", | ||
| "tsx": "4.20.5", | ||
| "typescript": "5.9.2" | ||
| }, |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Remove @types/dotenv (dotenv ships its own types and v17 is ESM).
The legacy typings can conflict with v17 and ESM usage.
"devDependencies": {
- "@types/dotenv": "8.2.3",
"@types/node": "24.3.0",
"playwright": "1.55.0",
"tsx": "4.20.5",
"typescript": "5.9.2"
},📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "@types/node": "24.3.0", | |
| "playwright": "1.55.0", | |
| "tsx": "4.20.5", | |
| "typescript": "5.9.2" | |
| }, | |
| "devDependencies": { | |
| "@types/node": "24.3.0", | |
| "playwright": "1.55.0", | |
| "tsx": "4.20.5", | |
| "typescript": "5.9.2" | |
| }, |
🤖 Prompt for AI Agents
In apps/unsubscriber/package.json around lines 15 to 19, remove the legacy
"@types/dotenv" entry from dependencies/devDependencies (dotenv v17 includes its
own types and the @types package can conflict with ESM). After removing the
entry, run your package manager (npm/yarn/pnpm) to update node_modules and the
lockfile, and verify no code imports reference "@types/dotenv"; if CI uses a
cached lockfile update that as well.
| date: new Date().toISOString(), | ||
| subject, | ||
| // ...message, |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
ParsedMessage shape changed — scan for stale call sites.
You added top-level date and subject. Verify code no longer relies on removed/old locations (e.g., headers.subject or sizeEstimate).
Run:
🏁 Script executed:
#!/bin/bash
# Find legacy access patterns
rg -nP -C2 '\bsizeEstimate\b|headers\.(subject|date)|\bmessage\.headers\.(subject|date)\b'Length of output: 24708
Replace legacy headers and sizeEstimate references with new top-level fields
Multiple components, utilities, and tests still reference message.headers.subject / .date and the now-removed sizeEstimate. Refactor all occurrences to use the top-level subject and date props on ParsedMessage, and remove or update any sizeEstimate usages in datasources, types, and tests.
🤖 Prompt for AI Agents
In apps/web/__tests__/ai-choose-args.test.ts around lines 234 to 236, the test
still constructs or asserts against legacy nested fields like
message.headers.subject and message.headers.date and uses the removed
sizeEstimate; update the test to set and assert top-level ParsedMessage
properties subject and date (e.g., subject: ..., date: ...) instead of
message.headers.*, remove any references to sizeEstimate (or replace with the
new field if applicable), and search/replace other occurrences in this test file
(and related fixtures/mocks) to ensure all code and assertions use the new
top-level props and no longer reference sizeEstimate.
| expect(queryBatchMessages).toHaveBeenCalledWith( | ||
| expect.anything(), | ||
| expect.anything(), | ||
| expect.objectContaining({ | ||
| maxResults: expect.any(Number), | ||
| }), | ||
| ); |
There was a problem hiding this comment.
Fix incorrect mock assertion: queryBatchMessages has 2 args, not 3
The implementation calls queryBatchMessages(gmail, options). This 3-arg expectation will fail.
- expect(queryBatchMessages).toHaveBeenCalledWith(
- expect.anything(),
- expect.anything(),
- expect.objectContaining({
- maxResults: expect.any(Number),
- }),
- );
+ expect(queryBatchMessages).toHaveBeenCalledWith(
+ expect.anything(),
+ expect.objectContaining({
+ query: expect.stringContaining("-label:sent"),
+ maxResults: expect.any(Number),
+ }),
+ );📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| expect(queryBatchMessages).toHaveBeenCalledWith( | |
| expect.anything(), | |
| expect.anything(), | |
| expect.objectContaining({ | |
| maxResults: expect.any(Number), | |
| }), | |
| ); | |
| expect(queryBatchMessages).toHaveBeenCalledWith( | |
| expect.anything(), | |
| expect.objectContaining({ | |
| query: expect.stringContaining("-label:sent"), | |
| maxResults: expect.any(Number), | |
| }), | |
| ); |
🤖 Prompt for AI Agents
In apps/web/__tests__/ai-example-matches.test.ts around lines 96 to 102, the
test incorrectly asserts queryBatchMessages was called with 3 arguments but the
implementation calls queryBatchMessages(gmail, options) with 2; update the
expectation to assert two args only (first argument can remain expect.anything()
or a more specific matcher for gmail, second should be expect.objectContaining({
maxResults: expect.any(Number) })), removing the extra expect.anything() so the
toHaveBeenCalledWith matches the actual call signature.
| const result = await aiPromptToRules({ | ||
| emailAccount, | ||
| promptFile, | ||
| isEditing: false, | ||
| }); | ||
|
|
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Confirm intent to test the “old” implementation.
Calls here target aiPromptToRulesOld (aliased as aiPromptToRules). If the new implementation is the intended target post-upgrade, update the import to avoid stale coverage.
Run:
If the old impl is intentionally retained, consider renaming this test to ai-prompt-to-rules-old.test.ts for clarity.
🏁 Script executed:
#!/bin/bash
# Check current exports/usage to decide which impl should be tested
rg -nP -C2 'prompt-to-rules-old|prompt-to-rules\.ts' apps/webLength of output: 943
Align test with intended implementation
This test currently imports aiPromptToRulesOld. Choose one:
- If you intend to cover the new logic, update the import to
aiPromptToRulesfrom
@/utils/ai/rule/prompt-to-rules.ts. - If you’re deliberately testing the legacy version, rename the file to
ai-prompt-to-rules-old.test.tsfor clarity.
🤖 Prompt for AI Agents
In apps/web/__tests__/ai-prompt-to-rules.test.ts around lines 30 to 35, the test
imports the legacy aiPromptToRulesOld but the assertion calls aiPromptToRules;
either update the import to import aiPromptToRules from
"@/utils/ai/rule/prompt-to-rules.ts" so the test exercises the new logic, or if
you intend to test the legacy implementation, rename this test file to
ai-prompt-to-rules-old.test.ts and keep the import as aiPromptToRulesOld for
clarity and correct intent.
|
Claude finished @elie222's task —— View job Claude finished @elie222's task —— View job PR Review: Package UpgradesI've conducted a comprehensive review of this package upgrade PR. Here are my findings: Review Checklist
✅ What Looks GoodSystematic Upgrade Approach:
Security & Configuration:
Development Workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
apps/web/ee/billing/stripe/index.ts (2)
5-23: Fix return type to satisfy strict null checks and avoid nullable client.getStripe currently infers Stripe | null; tighten to return Stripe and avoid nullable state.
Apply:
-let stripe: Stripe | null = null; +let stripeClient: Stripe | undefined; -export const getStripe = () => { +export const getStripe = (): Stripe => { if (!env.STRIPE_SECRET_KEY) throw new Error("STRIPE_SECRET_KEY is not set"); - if (!stripe) { - stripe = new Stripe(env.STRIPE_SECRET_KEY, { + if (!stripeClient) { + stripeClient = new Stripe(env.STRIPE_SECRET_KEY, { apiVersion: "2025-08-27.basil", appInfo: { name: "Inbox Zero", version: "1.0.0", url: "https://www.getinboxzero.com", }, typescript: true, }); } - return stripe; + return stripeClient; }
66-71: Explicitly set proration_behavior and idempotency key; handle nullable quantity
- Pass a
proration_behavior(“create_prorations” / “none” / “always_invoice”) in the update params to control prorations.- Supply an idempotency key (e.g.
{ idempotencyKey:subItem:${subscriptionItemId}:qty:${quantityToSet}}) for safe retries.- Since
SubscriptionItem.quantityis nullable (always null for metered plans, nullable for tiered plans), adjust your change check to handlenull(for exampleif ((current.quantity ?? 0) !== quantityToSet)or skip updates on metered pricing).const updatedItem = await stripe.subscriptionItems.update( subscriptionItemId, - { quantity: quantityToSet }, + { + quantity: quantityToSet, + proration_behavior: "create_prorations", // or "none"/"always_invoice" + }, + { idempotencyKey: `subItem:${subscriptionItemId}:qty:${quantityToSet}` }, );
🧹 Nitpick comments (9)
docker/Dockerfile.prod (6)
32-32: Lock installs for reproducible builds.
--no-frozen-lockfileallows lockfile drift at build time. If patches don’t need lockfile edits (common with patch-package), prefer a frozen install.-RUN pnpm install --no-frozen-lockfile --prefer-offline +RUN pnpm install --frozen-lockfile --prefer-offlineIf you rely on
pnpm patchchanging the lockfile at build time, keep current behavior; otherwise, freezing is safer.
41-43: Disable Next telemetry in CI/builds.Small hardening/privacy win during image builds.
ENV NODE_ENV=production +ENV NEXT_TELEMETRY_DISABLED=1
34-36: Run the container as non-root.Reduce blast radius by switching to the precreated
nodeuser in the official image and ensuring permissions.COPY . . +RUN chown -R node:node /app @@ -# Use the simpler pnpm command, should work now as pnpm & next are installed -CMD pnpm --filter inbox-zero-ai start +# Use the simpler pnpm command, should work now as pnpm & next are installed +USER node +CMD pnpm --filter inbox-zero-ai startAlso applies to: 75-77
26-27: Potential duplicate Prisma generate.Comment implies a postinstall runs Prisma generate (hence copying schema before install), and there’s also an explicit
prisma generate. Running both wastes time and cache.
- If postinstall already runs
prisma generate, drop the explicitRUN pnpm --filter inbox-zero-ai exec -- prisma generate.- Otherwise, keep the explicit step and disable the postinstall hook for cleaner caching.
Also applies to: 39-39
29-33: Consider multi-stage + pnpm deploy to ship only prod deps.You currently install all dev deps in the final image. A multi-stage build with
pnpm fetch+pnpm install --frozen-lockfilein the builder, thenpnpm deploy --filter inbox-zero-ai --prod /appin the runner can materially shrink the image and speed cold starts.If helpful, I can sketch a multi-stage variant tailored to this workspace.
8-8: Switch to Corepack in Dockerfile.prod for deterministic pnpm@10.15.0 activationpackage.json is already pinned to
pnpm@10.15.0. In docker/Dockerfile.prod (line 8), replace:-RUN npm install -g pnpm@10.15.0 +RUN corepack enable && corepack prepare pnpm@10.15.0 --activateapps/web/ee/billing/stripe/index.ts (3)
13-20: Consider adding explicit network resiliency (timeouts/retries).To harden external calls, set conservative retries and a request timeout.
Apply:
typescript: true, + maxNetworkRetries: 2, + timeout: 60000,
45-57: Avoid shadowing the module-scoped client variable.Rename the local const for clarity and to prevent confusion with the cached singleton.
Apply:
- const stripe = getStripe(); + const stripeClient = getStripe(); - const currentItem = - await stripe.subscriptionItems.retrieve(subscriptionItemId); + const currentItem = + await stripeClient.subscriptionItems.retrieve(subscriptionItemId); @@ - const updatedItem = await stripe.subscriptionItems.update( + const updatedItem = await stripeClient.subscriptionItems.update( subscriptionItemId, { quantity: quantityToSet, }, );Also applies to: 66-74
32-33: Validate and normalize quantity to a safe integer ≥ 1.Prevents NaN/Infinity and fractional inputs from leaking into the Stripe call.
Apply:
- const quantityToSet = Math.max(1, quantity); + const requested = Number.isFinite(quantity) ? Math.floor(quantity) : 1; + const quantityToSet = Math.max(1, requested);
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
apps/web/ee/billing/stripe/index.ts(1 hunks)docker/Dockerfile.prod(1 hunks)version.txt(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- version.txt
🧰 Additional context used
📓 Path-based instructions (6)
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use@/for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX
Files:
apps/web/ee/billing/stripe/index.ts
!{.cursor/rules/*.mdc}
📄 CodeRabbit inference engine (.cursor/rules/cursor-rules.mdc)
Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location
Files:
apps/web/ee/billing/stripe/index.tsdocker/Dockerfile.prod
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)
**/*.ts: The same validation should be done in the server action too
Define validation schemas using Zod
Files:
apps/web/ee/billing/stripe/index.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
**/*.{ts,tsx}: UsecreateScopedLoggerfor logging in backend TypeScript files
Typically add the logger initialization at the top of the file when usingcreateScopedLogger
Only use.with()on a logger instance within a specific function, not for a global loggerImport Prisma in the project using
import prisma from "@/utils/prisma";
**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
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 parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.
Files:
apps/web/ee/billing/stripe/index.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useelements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...
Files:
apps/web/ee/billing/stripe/index.ts
!pages/_document.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.
Files:
apps/web/ee/billing/stripe/index.tsdocker/Dockerfile.prod
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-07-18T15:06:10.570Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Set appropriate timeouts for LLM calls in tests (e.g., 15,000ms for long-running operations)
📚 Learning: 2025-07-18T15:06:02.201Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/installing-packages.mdc:0-0
Timestamp: 2025-07-18T15:06:02.201Z
Learning: Use `pnpm` to install packages
Applied to files:
docker/Dockerfile.prod
⏰ 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: Jit Security
- GitHub Check: claude-review
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (1)
apps/web/ee/billing/stripe/index.ts (1)
13-13: Confirm Stripe API version bump consistency.apps/web/package.json pins stripe@18.5.0; no other apiVersion arguments remain. Verify stripe@18.5.0 supports "2025-08-27.basil".
|
Claude finished @elie222's task —— View job PR Review: Package UpgradesI've conducted a comprehensive review of this package upgrade PR. Here are my findings: Review Checklist
✅ What Looks GoodSystematic Upgrade Approach:
Security & Configuration:
Development Workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
apps/web/utils/ai/example-matches/find-example-matches.ts (3)
74-79: Harden listEmails execute with try/catchExternal Gmail calls can throw; add minimal error handling to avoid unhandled rejections and surface context.
Apply:
- execute: async ({ query }: { query: string | undefined }) => { - const { messages } = await queryBatchMessages(gmail, { - query: `${query || ""} -label:sent`.trim(), - maxResults: 20, - }); + execute: async ({ query }: { query: string | undefined }) => { + try { + const { messages } = await queryBatchMessages(gmail, { + query: `${query || ""} -label:sent`.trim(), + maxResults: 20, + }); + const results = messages.map((message) => ({ + emailId: message.id, + from: message.headers.from, + subject: message.headers.subject, + snippet: message.snippet, + })); + for (const result of results) { + listedEmails[result.emailId] = result; + } + return results; + } catch (err) { + throw new Error(`listEmails failed: ${(err as Error).message}`); + } - const results = messages.map((message) => ({ - emailId: message.id, - from: message.headers.from, - subject: message.headers.subject, - snippet: message.snippet, - })); - - for (const result of results) { - listedEmails[result.emailId] = result; - } - - return results; },
103-116: Handle AI call failures and add retryWrap
generateTextto catch/annotate failures; consider a small retry for transient errors per project AI guidelines.Apply:
- const aiResponse = await generateText({ - ...modelOptions, - system, - prompt, - stopWhen: stepCountIs(10), - tools: { - listEmails: listEmailsTool(gmail), - [FIND_EXAMPLE_MATCHES]: tool({ - description: "Find example matches", - inputSchema: findExampleMatchesSchema, - }), - }, - }); + let aiResponse; + try { + aiResponse = await generateText({ + ...modelOptions, + system, + prompt, + stopWhen: stepCountIs(10), + tools: { + listEmails: listEmailsTool(gmail), + [FIND_EXAMPLE_MATCHES]: tool({ + description: "Find example matches", + inputSchema: findExampleMatchesSchema, + }), + }, + }); + } catch (err) { + throw new Error(`AI findExampleMatches failed: ${(err as Error).message}`); + }Optionally wrap the call with your
withRetry(...)helper if available.
121-126: Use schema.parse instead of type assertion for runtime safetyAvoid trusting the cast; leverage the Zod schema you already defined.
Apply:
- >((acc, { input }) => { - const typedArgs = input as z.infer<typeof findExampleMatchesSchema>; - return acc.concat(typedArgs.matches); - }, []); + >((acc, { input }) => { + const { matches } = findExampleMatchesSchema.parse(input); + return acc.concat(matches); + }, []);
🧹 Nitpick comments (2)
apps/web/utils/ai/example-matches/find-example-matches.ts (2)
129-135: Preserve reason and (optionally) dedupe resultsIncluding
reasonhelps explain matches. Also consider deduping byemailId+rule.Apply minimal change to keep
reason:return { matches: matches .filter((match) => listedEmails[match.emailId]) .map((match) => ({ ...listedEmails[match.emailId], rule: match.rule, + reason: match.reason, })), };Optional dedupe (outside this hunk):
const uniq = new Map<string, typeof listedEmails[string] & { rule: string; reason?: string }>(); for (const m of matches) { if (!listedEmails[m.emailId]) continue; const key = `${m.emailId}::${m.rule}`; if (!uniq.has(key)) uniq.set(key, { ...listedEmails[m.emailId], rule: m.rule, reason: m.reason }); } return { matches: [...uniq.values()] };
30-36: Add explicit return type (clarity) and optionally a scoped loggerMake the API contract explicit and prepare for logging per backend guidelines.
Example:
-export async function aiFindExampleMatches( +type FindExampleMatchesResult = { + matches: Array<{ emailId: string; from: string; subject: string; snippet: string; rule: string; reason?: string }>; +}; +export async function aiFindExampleMatches( emailAccount: EmailAccountWithAI, gmail: gmail_v1.Gmail, rulesPrompt: string, -) { +): Promise<FindExampleMatchesResult> {Optionally initialize a scoped logger at the top (adjust import/path as in your project):
// import { createScopedLogger } from "@/utils/logger"; // const log = createScopedLogger("ai.findExampleMatches");
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
apps/web/utils/ai/example-matches/find-example-matches.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (9)
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use@/for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX
Files:
apps/web/utils/ai/example-matches/find-example-matches.ts
!{.cursor/rules/*.mdc}
📄 CodeRabbit inference engine (.cursor/rules/cursor-rules.mdc)
Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location
Files:
apps/web/utils/ai/example-matches/find-example-matches.ts
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)
**/*.ts: The same validation should be done in the server action too
Define validation schemas using Zod
Files:
apps/web/utils/ai/example-matches/find-example-matches.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
**/*.{ts,tsx}: UsecreateScopedLoggerfor logging in backend TypeScript files
Typically add the logger initialization at the top of the file when usingcreateScopedLogger
Only use.with()on a logger instance within a specific function, not for a global loggerImport Prisma in the project using
import prisma from "@/utils/prisma";
**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
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 parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.
Files:
apps/web/utils/ai/example-matches/find-example-matches.ts
apps/web/utils/**
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Create utility functions in
utils/folder for reusable logic
Files:
apps/web/utils/ai/example-matches/find-example-matches.ts
apps/web/utils/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
apps/web/utils/**/*.ts: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size
Files:
apps/web/utils/ai/example-matches/find-example-matches.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useelements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...
Files:
apps/web/utils/ai/example-matches/find-example-matches.ts
!pages/_document.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.
Files:
apps/web/utils/ai/example-matches/find-example-matches.ts
apps/web/utils/{ai,llms}/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/llm.mdc)
apps/web/utils/{ai,llms}/**/*.ts: Place LLM-related implementation code under apps/web/utils/ai or apps/web/utils/llms
Keep system and user prompts separate; system defines role/task, user contains data/context
Always validate LLM responses with a specific Zod schema
Use descriptive scoped loggers per feature and log inputs/outputs with appropriate levels and context
Implement early returns for invalid inputs and use proper error types with logging
Add fallbacks for AI failures and include retry logic for transient errors using withRetry
Format prompts with XML-like tags; remove excessive whitespace; truncate overly long inputs; keep formatting consistent
Use TypeScript types for all parameters/returns and define interfaces for complex IO structures
Keep related AI functions co-located; extract shared logic into utilities; document complex AI logic with clear comments
Call LLMs via createGenerateObject; pass system, prompt, and a Zod schema; return the validated result.object
Derive model options using getModel(...) and pass them to createGenerateObject and the generate call
Files:
apps/web/utils/ai/example-matches/find-example-matches.ts
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-07-18T15:06:10.570Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Set appropriate timeouts for LLM calls in tests (e.g., 15,000ms for long-running operations)
⏰ 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: Jit Security
- GitHub Check: claude-review
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (1)
apps/web/utils/ai/example-matches/find-example-matches.ts (1)
71-73: Update remainingparameters:schemas toinputSchema:
Found lingeringparameters:definitions in packages/tinybird/src/query.ts (line 9), apps/web/utils/ai/group/create-group.ts (line 37), and several choose-rule utilities—replace these withinputSchema:to align with the upgraded SDK.
|
Claude finished @elie222's task —— View job PR Review: Package UpgradesI've conducted a comprehensive review of this package upgrade PR. Here are my findings: Review Checklist
✅ What Looks GoodSystematic Upgrade Approach:
Security & Configuration:
Development Workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
apps/web/utils/llms/index.ts (1)
118-166: createGenerateObject doesn’t pass a model and misattributes usage; add model + fallback (parity with createGenerateText).Currently
generateObjectis called withoutmodel, and usage is saved againstmodelOptionsregardless of the actual model used. This can break calls and/or skew analytics.Apply:
export function createGenerateObject({ userEmail, label, modelOptions, }: { userEmail: string; label: string; modelOptions: ReturnType<typeof getModel>; }): typeof generateObject { return async (...args) => { - try { - const [options, ...restArgs] = args; - - logger.trace("Generating object", { - label, - system: options.system?.slice(0, MAX_LOG_LENGTH), - prompt: options.prompt?.slice(0, MAX_LOG_LENGTH), - }); - - const result = await generateObject( - { - ...options, - ...commonOptions, - }, - ...restArgs, - ); - - if (result.usage) { - await saveAiUsage({ - email: userEmail, - usage: result.usage, - provider: modelOptions.provider, - model: modelOptions.modelName, - label, - }); - } - - logger.trace("Generated object", { - label, - result: result.object, - }); - - return result; - } catch (error) { - await handleError(error, userEmail); - throw error; - } + const [options, ...restArgs] = args; + + const generate = async (model: LanguageModelV2) => { + logger.trace("Generating object", { + label, + system: options.system?.slice(0, MAX_LOG_LENGTH), + prompt: options.prompt?.slice(0, MAX_LOG_LENGTH), + }); + + const result = await generateObject( + { + ...options, + ...commonOptions, + model, + }, + ...restArgs, + ); + + if (result.usage) { + await saveAiUsage({ + email: userEmail, + usage: result.usage, + provider: modelOptions.provider, + model: modelOptions.modelName, + label, + }); + } + + logger.trace("Generated object", { label, result: result.object }); + return result; + }; + + try { + return await generate(modelOptions.model); + } catch (error) { + if ( + modelOptions.backupModel && + (isServiceUnavailableError(error) || isAWSThrottlingError(error)) + ) { + logger.warn("Using backup model (generateObject)", { + error, + model: modelOptions.backupModel, + }); + try { + return await generate(modelOptions.backupModel); + } catch (e) { + await handleError(e, userEmail); + throw e; + } + } + await handleError(error, userEmail); + throw error; + } }; }apps/web/utils/ai/assistant/chat.ts (1)
735-737: Action type naming mismatch (“DRAFT_REPLY” vs enum “DRAFT_EMAIL”) will confuse the model and fail Zod validation.Align prompt text and examples with actual enum values (or add a server-side synonym map).
Apply:
- - The knowledge base is used to draft reply content. - - It is only used when an action of type DRAFT_REPLY is used AND the rule has no preset draft content. + - The knowledge base is used to draft reply content. + - It is only used when an action of type DRAFT_EMAIL is used AND the rule has no preset draft content.- { - "type": "draft", - "fields": { - "content": "{{draft a reply}}" - } - } + { + "type": "DRAFT_EMAIL", + "fields": { + "content": "{{draft a reply}}" + } + }If you prefer keeping “draft” in examples, implement a mapping layer to coerce “draft”/“DRAFT_REPLY” to
ActionType.DRAFT_EMAILbefore validation.Also applies to: 798-802
🧹 Nitpick comments (8)
apps/web/utils/ai/group/create-group.ts (2)
35-54: Wrap listEmails in tool(...) for consistency and schema enforcementUsing the SDK’s tool(...) wrapper here (like the other tools) keeps things uniform and ensures consistent validation/telemetry.
Apply:
-const listEmailsTool = (gmail: gmail_v1.Gmail) => ({ - description: "List email messages. Returns max 20 results.", - inputSchema: z.object({ - query: z.string().optional().describe("Optional Gmail search query."), - }), - execute: async ({ query }: { query: string | undefined }) => { - const { messages } = await queryBatchMessages(gmail, { - query: `${query || ""} -label:sent`.trim(), - maxResults: 20, - }); - - const results = messages.map((message) => ({ - from: message.headers.from, - subject: message.headers.subject, - snippet: message.snippet, - })); - - return results; - }, -}); +const listEmailsTool = (gmail: gmail_v1.Gmail) => + tool({ + description: "List email messages. Returns max 20 results.", + inputSchema: z.object({ + query: z.string().optional().describe("Optional Gmail search query."), + }), + execute: async ({ query }: { query?: string }) => { + const log = logger.with({ tool: "listEmails" }); + try { + const { messages } = await queryBatchMessages(gmail, { + query: `${query ?? ""} -label:sent`.trim(), + maxResults: 20, + }); + const results = messages.map((message) => ({ + from: message.headers.from, + subject: message.headers.subject, + snippet: message.snippet, + })); + log.debug({ count: results.length }, "Listed emails"); + return results; + } catch (err) { + log.warn({ err }, "Failed to list emails"); + return []; + } + }, + });
41-44: Prefer nullish coalescing and add lightweight guardrailsUse query ?? "" instead of || to avoid treating empty strings as falsy; also consider a simple length cap to prevent overly long queries from the model.
- query: `${query || ""} -label:sent`.trim(), + query: `${(query ?? "").slice(0, 256)} -label:sent`.trim(),packages/loops/src/loops.ts (2)
6-8: Replace console.warn with createScopedLogger per codebase guidelinesUse the project’s logger instead of console.* in backend TS files. Initialize a scoped logger once at the top and call logger.warn here.
+import { createScopedLogger } from "<update-to-your-logger-path>"; +const logger = createScopedLogger("packages/loops/loops"); if (!process.env.LOOPS_API_SECRET) { - console.warn("LOOPS_API_SECRET is not set"); + logger.warn("LOOPS_API_SECRET is not set"); return; }Please confirm the correct import path for createScopedLogger in this repo (guideline: use createScopedLogger for backend TS).
11-13: Environment var name: double-check against Loops docsDocs reference LOOPS_API_KEY for the SDK. If LOOPS_API_SECRET is intentional in your infra, ignore; otherwise align to KEY to prevent misconfig at runtime. (loops.so)
apps/web/app/api/ai/compose-autocomplete/route.ts (1)
12-12: Return proper HTTP status for unauthenticated case.Respond with 401 to aid clients and monitoring.
Apply:
- if (!user) return NextResponse.json({ error: "Not authenticated" }); + if (!user) { + return NextResponse.json({ error: "Not authenticated" }, { status: 401 }); + }apps/web/utils/ai/assistant/chat.ts (2)
719-721: Fix typos/grammar in the system prompt.Small polish improves instruction clarity.
Apply:
-You can set general infomation about the user too that will be passed as context when the AI is processing emails. -Reply Zero is a feature that labels emails that need a reply "To Reply". And labels emails that are awaiting a response "Awaiting". The also is also able to see these in a minimalist UI within Inbox Zero which only shows which emails the user needs to reply to or is awaiting a response on. +You can set general information about the user that will be passed as context when the AI processes emails. +Reply Zero labels emails that need a reply "To Reply" and emails awaiting a response "Awaiting". The user can also view these in a minimalist UI within Inbox Zero that shows only emails to reply to or awaiting a response.
899-906: Example contradicts “don’t use placeholders”; avoid @company.com.Either remove the placeholder or state a concrete domain to reduce ambiguity.
Apply one:
- "condition": { "static": { "from": "@company.com" } }, + "condition": { "static": { "from": "@inboxzero.com" } },or rephrase to avoid static placeholder:
- "condition": { "static": { "from": "@company.com" } }, + "condition": { "aiInstructions": "Emails from my company domain (ask the user if unknown)" },apps/web/app/api/ai/summarise/controller.ts (1)
35-38: Use a hashed Redis key instead of the raw prompt to reduce PII exposure and key length.Hashing the key avoids very long keys and leaking input content in keyspace.
Apply:
+import { createHash } from "node:crypto"; ... - onFinish: async (result) => { - await saveSummary(prompt, result.text); - await expire(prompt, 60 * 60 * 24); - }, + onFinish: async (result) => { + const key = + "summary:" + + createHash("sha256").update(prompt).digest("hex").slice(0, 32); + await saveSummary(key, result.text); + await expire(key, 60 * 60 * 24); + },
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (7)
apps/web/app/api/ai/compose-autocomplete/route.ts(1 hunks)apps/web/app/api/ai/summarise/controller.ts(1 hunks)apps/web/utils/ai/assistant/chat.ts(1 hunks)apps/web/utils/ai/group/create-group.ts(1 hunks)apps/web/utils/auth.ts(1 hunks)apps/web/utils/llms/index.ts(1 hunks)packages/loops/src/loops.ts(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- apps/web/utils/auth.ts
🧰 Additional context used
📓 Path-based instructions (14)
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/**/*.{ts,tsx}: Use TypeScript with strict null checks
Path aliases: Use@/for imports from project root
Use proper error handling with try/catch blocks
Format code with Prettier
Leverage TypeScript inference for better DX
Files:
apps/web/app/api/ai/compose-autocomplete/route.tsapps/web/utils/ai/group/create-group.tsapps/web/utils/ai/assistant/chat.tsapps/web/app/api/ai/summarise/controller.tsapps/web/utils/llms/index.ts
apps/web/app/**
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
NextJS app router structure with (app) directory
Files:
apps/web/app/api/ai/compose-autocomplete/route.tsapps/web/app/api/ai/summarise/controller.ts
apps/web/app/api/**/route.ts
📄 CodeRabbit inference engine (apps/web/CLAUDE.md)
apps/web/app/api/**/route.ts: UsewithAuthfor user-level operations
UsewithEmailAccountfor email-account-level operations
Do NOT use POST API routes for mutations - use server actions instead
No need for try/catch in GET routes when using middleware
Export response types from GET routes
apps/web/app/api/**/route.ts: Wrap all GET API route handlers withwithAuthorwithEmailAccountmiddleware for authentication and authorization.
Export response types from GET API routes for type-safe client usage.
Do not use try/catch in GET API routes when using authentication middleware; rely on centralized error handling.
Files:
apps/web/app/api/ai/compose-autocomplete/route.ts
!{.cursor/rules/*.mdc}
📄 CodeRabbit inference engine (.cursor/rules/cursor-rules.mdc)
Never place rule files in the project root, in subdirectories outside .cursor/rules, or in any other location
Files:
apps/web/app/api/ai/compose-autocomplete/route.tsapps/web/utils/ai/group/create-group.tsapps/web/utils/ai/assistant/chat.tspackages/loops/src/loops.tsapps/web/app/api/ai/summarise/controller.tsapps/web/utils/llms/index.ts
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/form-handling.mdc)
**/*.ts: The same validation should be done in the server action too
Define validation schemas using Zod
Files:
apps/web/app/api/ai/compose-autocomplete/route.tsapps/web/utils/ai/group/create-group.tsapps/web/utils/ai/assistant/chat.tspackages/loops/src/loops.tsapps/web/app/api/ai/summarise/controller.tsapps/web/utils/llms/index.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/logging.mdc)
**/*.{ts,tsx}: UsecreateScopedLoggerfor logging in backend TypeScript files
Typically add the logger initialization at the top of the file when usingcreateScopedLogger
Only use.with()on a logger instance within a specific function, not for a global loggerImport Prisma in the project using
import prisma from "@/utils/prisma";
**/*.{ts,tsx}: Don't use TypeScript enums.
Don't use TypeScript const enum.
Don't use the TypeScript directive @ts-ignore.
Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't use implicit any type on variable declarations.
Don't let variables evolve into any type through reassignments.
Don't use non-null assertions with the ! postfix operator.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use export type for types.
Use import type for types.
Don't declare empty interfaces.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Don't use TypeScript namespaces.
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 parameter properties in class constructors.
Use either T[] or Array consistently.
Initialize each enum member value explicitly.
Make sure all enum members are literal values.
Files:
apps/web/app/api/ai/compose-autocomplete/route.tsapps/web/utils/ai/group/create-group.tsapps/web/utils/ai/assistant/chat.tspackages/loops/src/loops.tsapps/web/app/api/ai/summarise/controller.tsapps/web/utils/llms/index.ts
**/api/**/route.ts
📄 CodeRabbit inference engine (.cursor/rules/security.mdc)
**/api/**/route.ts: ALL API routes that handle user data MUST use appropriate authentication and authorization middleware (withAuth or withEmailAccount).
ALL database queries in API routes MUST be scoped to the authenticated user/account (e.g., include userId or emailAccountId in query filters).
Always validate that resources belong to the authenticated user before performing operations (resource ownership validation).
UsewithEmailAccountmiddleware for API routes that operate on a specific email account (i.e., use or requireemailAccountId).
UsewithAuthmiddleware for API routes that operate at the user level (i.e., use or require onlyuserId).
UsewithErrormiddleware (with proper validation) for public endpoints, custom authentication, or cron endpoints.
Cron endpoints MUST usewithErrormiddleware and validate the cron secret usinghasCronSecret(request)orhasPostCronSecret(request).
Cron endpoints MUST capture unauthorized attempts withcaptureExceptionand return a 401 status for unauthorized requests.
All parameters in API routes MUST be validated for type, format, and length before use.
Request bodies in API routes MUST be validated using Zod schemas before use.
All Prisma queries in API routes MUST only return necessary fields and never expose sensitive data.
Error messages in API routes MUST not leak internal information or sensitive data; use generic error messages and SafeError where appropriate.
API routes MUST use a consistent error response format, returning JSON with an error message and status code.
AllfindUniqueandfindFirstPrisma calls in API routes MUST include ownership filters (e.g., userId or emailAccountId).
AllfindManyPrisma calls in API routes MUST be scoped to the authenticated user's data.
Never use direct object references in API routes without ownership checks (prevent IDOR vulnerabilities).
Prevent mass assignment vulnerabilities by only allowing explicitly whitelisted fields in update operations in AP...
Files:
apps/web/app/api/ai/compose-autocomplete/route.ts
apps/web/app/api/**/*.{ts,js}
📄 CodeRabbit inference engine (.cursor/rules/security-audit.mdc)
apps/web/app/api/**/*.{ts,js}: All API route handlers in 'apps/web/app/api/' must use authentication middleware: withAuth, withEmailAccount, or withError (with custom authentication logic).
All Prisma queries in API routes must include user/account filtering (e.g., emailAccountId or userId in WHERE clauses) to prevent unauthorized data access.
All parameters used in API routes must be validated before use; do not use parameters from 'params' or request bodies directly in queries without validation.
Request bodies in API routes should use Zod schemas for validation.
API routes should only return necessary fields using Prisma's 'select' and must not include sensitive data in error messages.
Error messages in API routes must not reveal internal details; use generic errors and SafeError for user-facing errors.
All QStash endpoints (API routes called via publishToQstash or publishToQstashQueue) must use verifySignatureAppRouter to verify request authenticity.
All cron endpoints in API routes must use hasCronSecret or hasPostCronSecret for authentication.
Do not hardcode weak or plaintext secrets in API route files; secrets must not be directly assigned as string literals.
Review all new withError usage in API routes to ensure custom authentication is implemented where required.
Files:
apps/web/app/api/ai/compose-autocomplete/route.tsapps/web/app/api/ai/summarise/controller.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
**/*.{js,jsx,ts,tsx}: Don't useelements in Next.js projects.
Don't use elements in Next.js projects.
Don't use namespace imports.
Don't access namespace imports dynamically.
Don't use global eval().
Don't use console.
Don't use debugger.
Don't use var.
Don't use with statements in non-strict contexts.
Don't use the arguments object.
Don't use consecutive spaces in regular expression literals.
Don't use the comma operator.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names th...
Files:
apps/web/app/api/ai/compose-autocomplete/route.tsapps/web/utils/ai/group/create-group.tsapps/web/utils/ai/assistant/chat.tspackages/loops/src/loops.tsapps/web/app/api/ai/summarise/controller.tsapps/web/utils/llms/index.ts
!pages/_document.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/ultracite.mdc)
!pages/_document.{js,jsx,ts,tsx}: Don't import next/document outside of pages/_document.jsx in Next.js projects.
Don't import next/document outside of pages/_document.jsx in Next.js projects.
Files:
apps/web/app/api/ai/compose-autocomplete/route.tsapps/web/utils/ai/group/create-group.tsapps/web/utils/ai/assistant/chat.tspackages/loops/src/loops.tsapps/web/app/api/ai/summarise/controller.tsapps/web/utils/llms/index.ts
apps/web/utils/**
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
Create utility functions in
utils/folder for reusable logic
Files:
apps/web/utils/ai/group/create-group.tsapps/web/utils/ai/assistant/chat.tsapps/web/utils/llms/index.ts
apps/web/utils/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)
apps/web/utils/**/*.ts: Use lodash utilities for common operations (arrays, objects, strings)
Import specific lodash functions to minimize bundle size
Files:
apps/web/utils/ai/group/create-group.tsapps/web/utils/ai/assistant/chat.tsapps/web/utils/llms/index.ts
apps/web/utils/{ai,llms}/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/llm.mdc)
apps/web/utils/{ai,llms}/**/*.ts: Place LLM-related implementation code under apps/web/utils/ai or apps/web/utils/llms
Keep system and user prompts separate; system defines role/task, user contains data/context
Always validate LLM responses with a specific Zod schema
Use descriptive scoped loggers per feature and log inputs/outputs with appropriate levels and context
Implement early returns for invalid inputs and use proper error types with logging
Add fallbacks for AI failures and include retry logic for transient errors using withRetry
Format prompts with XML-like tags; remove excessive whitespace; truncate overly long inputs; keep formatting consistent
Use TypeScript types for all parameters/returns and define interfaces for complex IO structures
Keep related AI functions co-located; extract shared logic into utilities; document complex AI logic with clear comments
Call LLMs via createGenerateObject; pass system, prompt, and a Zod schema; return the validated result.object
Derive model options using getModel(...) and pass them to createGenerateObject and the generate call
Files:
apps/web/utils/ai/group/create-group.tsapps/web/utils/ai/assistant/chat.tsapps/web/utils/llms/index.ts
apps/web/utils/llms/index.ts
📄 CodeRabbit inference engine (.cursor/rules/llm.mdc)
Keep core LLM functionality centralized in apps/web/utils/llms/index.ts
Files:
apps/web/utils/llms/index.ts
🧠 Learnings (3)
📓 Common learnings
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm-test.mdc:0-0
Timestamp: 2025-07-18T15:06:10.570Z
Learning: Applies to apps/web/__tests__/**/*.test.ts : Set appropriate timeouts for LLM calls in tests (e.g., 15,000ms for long-running operations)
📚 Learning: 2025-08-17T16:57:25.834Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-08-17T16:57:25.834Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Keep system and user prompts separate; system defines role/task, user contains data/context
Applied to files:
apps/web/app/api/ai/compose-autocomplete/route.tsapps/web/utils/ai/assistant/chat.tsapps/web/app/api/ai/summarise/controller.tsapps/web/utils/llms/index.ts
📚 Learning: 2025-08-17T16:57:25.834Z
Learnt from: CR
PR: elie222/inbox-zero#0
File: .cursor/rules/llm.mdc:0-0
Timestamp: 2025-08-17T16:57:25.834Z
Learning: Applies to apps/web/utils/{ai,llms}/**/*.ts : Format prompts with XML-like tags; remove excessive whitespace; truncate overly long inputs; keep formatting consistent
Applied to files:
apps/web/utils/llms/index.ts
⏰ 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: Docker Scan
- GitHub Check: Static Code Analysis Js
- GitHub Check: Jit Security
- GitHub Check: claude-review
🔇 Additional comments (7)
apps/web/utils/ai/group/create-group.ts (2)
37-40: Switch to inputSchema looks correctThis aligns the tool with the SDK’s current API and the pattern used elsewhere. No issues.
98-104: Confirm mixed tool definitions interoperate
No legacyparameters:usage remains in anytool()calls, butlistEmailsTool(and similar helpers) still return plain objects withinputSchema. Run an integration smoke test to ensure plain-object tools andtool()-wrapped tools interoperate as expected—or refactorlistEmailsToolto usetool()for consistency.packages/loops/src/loops.ts (1)
23-31: createContact signature correct for v6.0.0 The Loops JS SDK v6.0.0+ switched to a single-object parameter for createContact, so the existing call is valid.Likely an incorrect or invalid review comment.
apps/web/app/api/ai/compose-autocomplete/route.ts (1)
23-32: Good migration to chat-style messages.System first, then user — correct ordering for most providers.
apps/web/utils/llms/index.ts (1)
181-181: API change to require messages: ModelMessage[] looks good.Matches upstream usage and related call sites.
apps/web/utils/ai/assistant/chat.ts (1)
947-953: Correct adoption of messages array with system preface.This maintains deterministic behavior across providers.
apps/web/app/api/ai/summarise/controller.ts (1)
23-32: Messages array migration LGTM.Structure and order look correct.
Summary by CodeRabbit