Conversation
Bug ReportThe Comments? Email us. This is the last day of your free trial. Subscribe at jazzberry.ai. |
WalkthroughThis update replaces the previous workflow execution logic with a new Changes
Sequence Diagram(s)sequenceDiagram
participant Queue
participant WorkflowRunner (DO)
participant DB
participant GmailAgent
participant WorkflowEngine
Queue->>WorkflowRunner (DO): runMainWorkflow(params)
alt Provider is Google
WorkflowRunner->>DB: getConnectionInfo
WorkflowRunner->>GmailAgent: fetchHistory
WorkflowRunner->>WorkflowRunner: runZeroWorkflow(params)
WorkflowRunner->>WorkflowEngine: runThreadWorkflow for each thread
else Provider unsupported
WorkflowRunner-->>Queue: Return UnsupportedWorkflowError
end
WorkflowRunner-->>Queue: Return result or error
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Poem
📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (6)
💤 Files with no reviewable changes (1)
🧰 Additional context used📓 Path-based instructions (3)**/*.{js,jsx,ts,tsx}📄 CodeRabbit Inference Engine (AGENT.md)
Files:
**/*.{js,jsx,ts,tsx,css}📄 CodeRabbit Inference Engine (AGENT.md)
Files:
**/*.{ts,tsx}📄 CodeRabbit Inference Engine (AGENT.md)
Files:
🧠 Learnings (2)apps/server/src/thread-workflow-utils/workflow-engine.ts (1)Learnt from: retrogtx apps/server/src/main.ts (1)Learnt from: CR ⏰ 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). (1)
🔇 Additional comments (9)
✨ 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. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
This stack of pull requests is managed by Graphite. Learn more about stacking. |
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
There was a problem hiding this comment.
Actionable comments posted: 3
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (6)
apps/server/src/main.ts(3 hunks)apps/server/src/pipelines.effect.ts(0 hunks)apps/server/src/pipelines.ts(2 hunks)apps/server/src/thread-workflow-utils/workflow-engine.ts(9 hunks)apps/server/src/thread-workflow-utils/workflow-functions.ts(3 hunks)apps/server/wrangler.jsonc(6 hunks)
💤 Files with no reviewable changes (1)
- apps/server/src/pipelines.effect.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (AGENT.md)
**/*.{js,jsx,ts,tsx}: Use 2-space indentation
Use single quotes
Limit lines to 100 characters in width
Semicolons are required
Files:
apps/server/src/main.tsapps/server/src/thread-workflow-utils/workflow-functions.tsapps/server/src/thread-workflow-utils/workflow-engine.tsapps/server/src/pipelines.ts
**/*.{js,jsx,ts,tsx,css}
📄 CodeRabbit Inference Engine (AGENT.md)
Use Prettier with sort-imports and Tailwind plugins
Files:
apps/server/src/main.tsapps/server/src/thread-workflow-utils/workflow-functions.tsapps/server/src/thread-workflow-utils/workflow-engine.tsapps/server/src/pipelines.ts
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (AGENT.md)
Enable TypeScript strict mode
Files:
apps/server/src/main.tsapps/server/src/thread-workflow-utils/workflow-functions.tsapps/server/src/thread-workflow-utils/workflow-engine.tsapps/server/src/pipelines.ts
🧠 Learnings (1)
apps/server/src/main.ts (1)
Learnt from: CR
PR: Mail-0/Zero#0
File: AGENT.md:0-0
Timestamp: 2025-07-21T20:24:46.841Z
Learning: Use Cloudflare Workers for backend deployment
🧬 Code Graph Analysis (2)
apps/server/src/main.ts (1)
apps/server/src/types.ts (1)
IThreadBatch(14-18)
apps/server/src/thread-workflow-utils/workflow-engine.ts (1)
apps/server/src/thread-workflow-utils/workflow-functions.ts (1)
workflowFunctions(19-531)
🪛 GitHub Actions: autofix.ci
apps/server/src/main.ts
[warning] 18-18: ESLint (no-unused-vars): Identifier 'EWorkflowType' is imported but never used. Consider removing this import.
[warning] 18-18: ESLint (no-unused-vars): Type 'MainWorkflowError' is imported but never used. Consider removing this import.
[warning] 42-42: ESLint (no-unused-vars): Identifier 'Effect' is imported but never used. Consider removing this import.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: cubic · AI code reviewer
- GitHub Check: Cursor Bugbot
🔇 Additional comments (9)
apps/server/wrangler.jsonc (1)
50-53: LGTM! The durable object configuration is properly set up across all environments.The WORKFLOW_RUNNER binding and migration entries are consistently configured with proper sequential versioning. This is exactly what you need for deploying a new Durable Object class.
Also applies to: 101-104, 210-213, 271-274, 383-386, 438-441
apps/server/src/main.ts (1)
751-767: Excellent refactor to Durable Objects for workflow execution!The implementation properly instantiates the WorkflowRunner with a unique ID for each batch, ensuring proper isolation. This is a solid architectural improvement that will help with concurrency and state management.
apps/server/src/thread-workflow-utils/workflow-functions.ts (3)
24-39: Smart implementation of workflow execution tracking!Using KV storage with a 1-hour TTL is a brilliant way to prevent duplicate workflow executions. This will significantly improve reliability when dealing with concurrent webhook notifications.
145-172: Excellent optimization with batch fetching!Breaking down the vectorized message fetching into batches of 20 with a concurrency limit of 3 is a solid approach. This prevents memory issues and API rate limits while maintaining good performance. The error handling that continues with empty results for failed batches is particularly robust.
293-301: Clean implementation of workflow execution cleanup.This ensures workflows can be re-executed after completion, which is essential for handling retries or reprocessing scenarios.
apps/server/src/thread-workflow-utils/workflow-engine.ts (1)
130-136: Well-structured integration of execution tracking in workflow definitions!Each workflow properly checks execution status at the start and cleans up at the end. The conditional execution of subsequent steps based on the check result is a clean pattern. This ensures workflows are idempotent and can handle duplicate triggers gracefully.
Also applies to: 187-194, 202-208, 235-242, 250-256, 284-291, 299-305, 333-340
apps/server/src/pipelines.ts (3)
126-194: Phenomenal implementation of the WorkflowRunner Durable Object!The structured error handling with typed unions and Effect library is top-notch. This provides excellent observability and makes debugging much easier. The validation logic is robust and the logging is comprehensive.
196-515: The zero workflow implementation is incredibly well thought out!Excellent use of atomic locks to prevent race conditions, proper concurrency control with
allSuccesses, and comprehensive error handling. The optimization of label change processing with single-pass functional approach is particularly elegant.
517-687: Thread workflow implementation is solid!Great integration with the workflow engine, proper context management, and comprehensive error handling. The cleanup of processing flags in both success and error paths ensures system reliability.
| }); | ||
|
|
||
| yield* Effect.tryPromise({ | ||
| try: async () => conn.end(), |
There was a problem hiding this comment.
🧹 Nitpick (assertive)
Minor redundancy in database connection cleanup.
You're calling conn.end() twice in the thread workflow. Consider wrapping the entire workflow logic in a try-finally block or using Effect's bracket pattern to ensure single cleanup.
Consider using Effect's acquireRelease pattern:
const withConnection = Effect.acquireRelease(
Effect.sync(() => createDb(env.HYPERDRIVE.connectionString)),
({ conn }) => Effect.promise(() => conn.end())
);Also applies to: 650-650
🤖 Prompt for AI Agents
In apps/server/src/pipelines.ts at lines 546 and 650, the database connection
cleanup calls conn.end() twice, causing redundancy. Refactor the workflow to use
Effect's acquireRelease or bracket pattern to manage the connection lifecycle,
acquiring the connection once and ensuring conn.end() is called exactly once in
the release phase. Wrap the entire workflow logic inside this pattern to
guarantee single cleanup and avoid duplicate calls.
There was a problem hiding this comment.
Bugbot free trial expires on July 29, 2025
Learn more in the Cursor dashboard.
| } | ||
|
|
||
| this.markWorkflowExecuted(workflowName, context.threadId); | ||
|
|
There was a problem hiding this comment.
Bug: Workflow Tracking Fails Due to Unique IDs
The workflow execution tracking in WorkflowEngine is broken. The use of env.WORKFLOW_RUNNER.get(env.WORKFLOW_RUNNER.newUniqueId()) creates a new, isolated WorkflowRunner Durable Object instance for each thread workflow execution. This causes the in-memory executedWorkflows Set within the WorkflowEngine to be reset for every execution, rendering the duplicate execution prevention logic ineffective. This also defeats the purpose of using Durable Objects for state management and adds unnecessary overhead. A deterministic ID (e.g., based on threadId) should be used for WorkflowRunner instantiation to ensure state persistence.
Locations (2)
| yield* Effect.tryPromise({ | ||
| try: async () => conn.end(), | ||
| catch: (error) => ({ _tag: 'DatabaseError' as const, error }), | ||
| }); |
There was a problem hiding this comment.
Bug: Double Connection Closure Causes Pool Errors
The database connection in runZeroWorkflow is closed twice: once directly after fetching the connection, and again in a subsequent block. This causes errors when attempting to close an already-closed connection and can lead to connection pool issues.
Locations (1)
There was a problem hiding this comment.
cubic analysis
8 issues found across 6 files • Review in cubic
React with 👍 or 👎 to teach cubic. You can also tag @cubic-dev-ai to give feedback, ask questions, or re-run the review.
| labelChange.labelIds.forEach((labelId) => targetSet.add(labelId)); | ||
| }; | ||
|
|
||
| history.forEach((historyItem) => { |
There was a problem hiding this comment.
Rule violated: Detect Typescript Performance Bottlenecks
Nested forEach loops iterate over the dynamic Gmail history array and, within each item, iterate again over potentially large messagesAdded/labels arrays. This results in quadratic complexity (O(n×m)) and creates a serious performance bottleneck, violating the "Avoid nesting loops over non-trivial collections" rule.
Prompt for AI agents
Address the following comment on apps/server/src/pipelines.ts at line 316:
<comment>Nested forEach loops iterate over the dynamic Gmail history array and, within each item, iterate again over potentially large messagesAdded/labels arrays. This results in quadratic complexity (O(n×m)) and creates a serious performance bottleneck, violating the "Avoid nesting loops over non-trivial collections" rule.</comment>
<file context>
@@ -49,18 +87,601 @@ export type WorkflowParams =
| { workflowType: 'thread'; params: ThreadWorkflowParams }
| { workflowType: 'zero'; params: ZeroWorkflowParams };
-export const runWorkflow = (
- workflowType: EWorkflowType,
- params: MainWorkflowParams | ThreadWorkflowParams | ZeroWorkflowParams,
-): Effect.Effect<string, any> => {
- switch (workflowType) {
- case EWorkflowType.MAIN:
</file context>
| return { alreadyExecuted: true }; | ||
| } | ||
|
|
||
| await env.gmail_processing_threads.put(workflowKey, Date.now().toString(), { |
There was a problem hiding this comment.
A race condition can allow multiple concurrent workers to pass the alreadyExecuted check because the get/put sequence is not atomic, resulting in duplicate workflow execution.
Prompt for AI agents
Address the following comment on apps/server/src/thread-workflow-utils/workflow-functions.ts at line 33:
<comment>A race condition can allow multiple concurrent workers to pass the `alreadyExecuted` check because the get/put sequence is not atomic, resulting in duplicate workflow execution.</comment>
<file context>
@@ -21,6 +21,23 @@ export const workflowFunctions: Record<string, WorkflowFunction> = {
return shouldGenerateDraft(context.thread, context.foundConnection);
},
+ checkWorkflowExecution: async (context) => {
+ const workflowKey = `workflow_${context.threadId}`;
+ const lastExecution = await env.gmail_processing_threads.get(workflowKey);
+
+ if (lastExecution) {
+ console.log('[WORKFLOW_FUNCTIONS] Workflow already executed for thread:', context.threadId);
</file context>
| }); | ||
|
|
||
| yield* Effect.tryPromise({ | ||
| try: async () => conn.end(), |
There was a problem hiding this comment.
Connection is closed twice; the second conn.end() may throw or hide other errors.
Prompt for AI agents
Address the following comment on apps/server/src/pipelines.ts at line 254:
<comment>Connection is closed twice; the second `conn.end()` may throw or hide other errors.</comment>
<file context>
@@ -49,18 +87,601 @@ export type WorkflowParams =
| { workflowType: 'thread'; params: ThreadWorkflowParams }
| { workflowType: 'zero'; params: ZeroWorkflowParams };
-export const runWorkflow = (
- workflowType: EWorkflowType,
- params: MainWorkflowParams | ThreadWorkflowParams | ZeroWorkflowParams,
-): Effect.Effect<string, any> => {
- switch (workflowType) {
- case EWorkflowType.MAIN:
</file context>
| * @returns | ||
| */ | ||
| public runMainWorkflow(params: MainWorkflowParams) { | ||
| return Effect.gen(this, function* () { |
There was a problem hiding this comment.
Incorrect use of Effect.gen; the first argument should be the generator function, not this, which will throw at runtime.
Prompt for AI agents
Address the following comment on apps/server/src/pipelines.ts at line 138:
<comment>Incorrect use of `Effect.gen`; the first argument should be the generator function, not `this`, which will throw at runtime.</comment>
<file context>
@@ -49,18 +87,601 @@ export type WorkflowParams =
| { workflowType: 'thread'; params: ThreadWorkflowParams }
| { workflowType: 'zero'; params: ZeroWorkflowParams };
-export const runWorkflow = (
- workflowType: EWorkflowType,
- params: MainWorkflowParams | ThreadWorkflowParams | ZeroWorkflowParams,
-): Effect.Effect<string, any> => {
- switch (workflowType) {
- case EWorkflowType.MAIN:
</file context>
| return Effect.gen(this, function* () { | |
| return Effect.gen(function* () { |
|
|
||
| export class WorkflowEngine { | ||
| private workflows: Map<string, WorkflowDefinition> = new Map(); | ||
| private executedWorkflows: Set<string> = new Set(); |
There was a problem hiding this comment.
executedWorkflows is never pruned, leading to unbounded memory growth for long-lived processes.
Prompt for AI agents
Address the following comment on apps/server/src/thread-workflow-utils/workflow-engine.ts at line 33:
<comment>`executedWorkflows` is never pruned, leading to unbounded memory growth for long-lived processes.</comment>
<file context>
@@ -30,6 +30,7 @@ export type WorkflowDefinition = {
export class WorkflowEngine {
private workflows: Map<string, WorkflowDefinition> = new Map();
+ private executedWorkflows: Set<string> = new Set();
registerWorkflow(workflow: WorkflowDefinition) {
</file context>
| } | ||
|
|
||
| if (this.isWorkflowExecuted(workflowName, context.threadId)) { | ||
| console.log( |
There was a problem hiding this comment.
Raw console.log statements should be replaced with the project’s logging utility to maintain structured and level-controlled logs.
Prompt for AI agents
Address the following comment on apps/server/src/thread-workflow-utils/workflow-engine.ts at line 67:
<comment>Raw `console.log` statements should be replaced with the project’s logging utility to maintain structured and level-controlled logs.</comment>
<file context>
@@ -48,6 +63,15 @@ export class WorkflowEngine {
throw new Error(`Workflow "${workflowName}" not found`);
}
+ if (this.isWorkflowExecuted(workflowName, context.threadId)) {
+ console.log(
+ `[WORKFLOW_ENGINE] Workflow "${workflowName}" already executed for thread "${context.threadId}", skipping`,
+ );
</file context>
| description: 'Determines if a draft should be generated for this thread', | ||
| enabled: true, | ||
| condition: async (context) => { | ||
| const executionCheck = context.results?.get('check-workflow-execution'); |
There was a problem hiding this comment.
context.results is always undefined inside step.condition so this condition can never work, meaning the duplicate-execution checks will silently fail.
Prompt for AI agents
Address the following comment on apps/server/src/thread-workflow-utils/workflow-engine.ts at line 143:
<comment>`context.results` is always undefined inside `step.condition` so this condition can never work, meaning the duplicate-execution checks will silently fail.</comment>
<file context>
@@ -91,12 +127,23 @@ export const createDefaultWorkflows = (): WorkflowEngine => {
name: 'auto-draft-generation',
description: 'Automatically generates drafts for threads that require responses',
steps: [
+ {
+ id: 'check-workflow-execution',
+ name: 'Check Workflow Execution',
+ description: 'Checks if this workflow has already been executed for this thread',
+ enabled: true,
+ action: workflowFunctions.checkWorkflowExecution,
</file context>
ec2f9b2 to
7b6a46c
Compare

Refactored workflow processing with Durable Objects
Description
Refactored the workflow processing system to use Durable Objects for better concurrency and reliability. Created a new
WorkflowRunnerDurable Object that encapsulates the workflow execution logic, replacing the previous function-based approach. This change improves thread processing by providing better isolation and state management.The implementation includes:
WorkflowRunnerDurable Object that handles main, zero, and thread workflowsType of Change
Areas Affected
Testing Done
Checklist
Additional Notes
This change should significantly improve the reliability of thread processing by preventing race conditions and providing better isolation between workflow executions. The Durable Object approach also allows for better scaling as each workflow execution gets its own isolated environment.
By submitting this pull request, I confirm that my contribution is made under the terms of the project's license.
Summary by CodeRabbit
New Features
WorkflowRunnerclass for improved workflow management and error handling.Improvements
Chores