Skip to content

feat(core): real time progress update for runs#937

Merged
amhsirak merged 6 commits into
developfrom
run-progress
Jan 4, 2026
Merged

feat(core): real time progress update for runs#937
amhsirak merged 6 commits into
developfrom
run-progress

Conversation

@amhsirak
Copy link
Copy Markdown
Member

@amhsirak amhsirak commented Jan 2, 2026

Summary by CodeRabbit

  • New Features

    • Real-time execution progress tracking: reports current action, total actions, and completion percentage; emits initial 0-of-total at run start and incremental updates (also sent via server socket).
  • Refactor

    • Removed automatic auto-scroll when expanding log rows — expanding a row no longer scrolls the log to the bottom.

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

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 2, 2026

Warning

Rate limit exceeded

@amhsirak has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 3 minutes and 32 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 0e3d430 and 292271f.

📒 Files selected for processing (2)
  • src/components/run/ColapsibleRow.tsx
  • src/components/run/RunContent.tsx

Walkthrough

Adds progress reporting to the Interpreter via a new progressUpdate(current, total, percentage) callback and internal totalActions/executedActions counters (initialized at run start and emitted at start and after each action). Also removes automatic scroll-to-bottom behavior when expanding rows in the run log UI.

Changes

Cohort / File(s) Summary
Core interpreter
maxun-core/src/interpret.ts
Added progressUpdate(current, total, percentage) to InterpreterOptions.debugChannel. Introduced private totalActions and private executedActions. Initialize totals at run() start and emit initial progress (0). Increment executedActions, compute percentage, and call progressUpdate after each action removal in the run loop.
Server integration
server/src/workflow-management/classes/Interpreter.ts
Pass progressUpdate into server interpreter options and emit workflowProgress via socket on callbacks so client(s) receive real-time progress updates.
UI run log
src/components/run/ColapsibleRow.tsx
Removed automatic scrolling behavior when expanding a row: deleted scrollToLogBottom and its invocation; removed the related auto-scroll useEffect. Expanding rows no longer auto-scrolls to log bottom.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Client
  participant ServerInterpreter as Server Interpreter
  participant CoreInterpreter as Core Interpreter
  participant Socket as SocketEmitter

  Client->>ServerInterpreter: Start workflow
  ServerInterpreter->>CoreInterpreter: run(workflow, options with progressUpdate)
  Note right of CoreInterpreter: initialize totalActions = N\nemit progressUpdate(0,N,0)
  loop for each action
    CoreInterpreter->>CoreInterpreter: execute action & remove from queue
    CoreInterpreter-->>ServerInterpreter: progressUpdate(current, total, pct)
    ServerInterpreter->>Socket: emit workflowProgress
    Socket->>Client: workflowProgress update
  end
  Note right of CoreInterpreter: final emit => 100%
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

Type: Enhancement, Scope: Ext

Poem

🐰 I hopped through code with tiny paws,
Counting actions, skipping flaws.
From zero steps to ninety-nine,
Progress hums in steady line.
Logs stay put — no scrolling paws.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(core): real time progress update for runs' directly and accurately describes the main objective of the pull request - adding real-time progress updates during workflow interpretation.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

❤️ Share

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between adfb12e and 36b80da.

📒 Files selected for processing (1)
  • maxun-core/src/interpret.ts
🔇 Additional comments (2)
maxun-core/src/interpret.ts (2)

51-51: LGTM! Progress callback interface is well-defined.

The progressUpdate callback follows the existing pattern and provides clear semantics for progress reporting.


1705-1710: Verify state cleanup for progress counters.

The initialization logic is correct, but the cleanup() method (lines 1737-1772) doesn't reset totalActions and executedActions. While the check at lines 1697-1699 prevents concurrent runs, adding resets to cleanup() would ensure consistent state management.

🔎 Suggested addition to cleanup() method

Add this to the cleanup() method after line 1765:

       // Reset state
       this.isAborted = false;
       this.initializedWorkflow = null;
+      this.totalActions = 0;
+      this.executedActions = 0;

Comment on lines +88 to +90
private totalActions: number = 0;

private executedActions: number = 0;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: Race condition on shared progress counters.

The executedActions and totalActions fields are accessed by multiple concurrent jobs (via enqueueLinks, popup handlers, and the concurrency manager) without synchronization. The increment at line 1605 is not atomic, leading to race conditions where updates can be lost between await points.

Impact:

  • Progress counts will be inaccurate or corrupted
  • Progress percentages may exceed 100% or never reach it
  • User-facing progress UI will display incorrect values

Recommendation: Use atomic operations or introduce a lock/mutex around progress updates, or track progress per-job and aggregate at completion.

🔎 Proposed fix using a simple lock pattern
+  private progressLock: Promise<void> = Promise.resolve();
+
   private totalActions: number = 0;

   private executedActions: number = 0;

Then wrap the progress update in line 1604-1614:

-          this.executedActions++;
-          const percentage = Math.round((this.executedActions / this.totalActions) * 100);
-
-          if (this.options.debugChannel?.progressUpdate) {
-            this.options.debugChannel.progressUpdate(
-              this.executedActions,
-              this.totalActions,
-              percentage
-            );
-          }
+          // Serialize progress updates to prevent race conditions
+          this.progressLock = this.progressLock.then(async () => {
+            this.executedActions++;
+            const percentage = Math.round((this.executedActions / this.totalActions) * 100);
+
+            if (this.options.debugChannel?.progressUpdate) {
+              this.options.debugChannel.progressUpdate(
+                this.executedActions,
+                this.totalActions,
+                percentage
+              );
+            }
+          });

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

Comment on lines +1604 to +1614

this.executedActions++;
const percentage = Math.round((this.executedActions / this.totalActions) * 100);

if (this.options.debugChannel?.progressUpdate) {
this.options.debugChannel.progressUpdate(
this.executedActions,
this.totalActions,
percentage
);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Consider counting failed actions in progress tracking.

Currently, when an action fails (line 1627-1629), the code continues without incrementing executedActions. This means:

  • Failed actions are not reflected in progress
  • Progress may never reach 100% if any actions fail
  • Users won't know if the workflow is stuck or still progressing

If this is intentional (only counting successful actions), consider documenting this behavior. Otherwise, consider incrementing progress for failed actions as well.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 36b80da and 2f42914.

📒 Files selected for processing (1)
  • server/src/workflow-management/classes/Interpreter.ts

Comment on lines +583 to +589
progressUpdate: (current: number, total: number, percentage: number) => {
this.socket.nsp.emit('workflowProgress', {
current,
total,
percentage
});
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add progressUpdate callback to editor mode for consistency.

The progressUpdate callback is added to InterpretRecording but is missing from interpretRecordingInEditor (line 251-263). This creates an inconsistent experience where progress updates work in run mode but not in editor mode.

🔎 Proposed fix to add progressUpdate to editor mode

Add the progressUpdate callback to the debugChannel options in interpretRecordingInEditor:

       debugChannel: {
         activeId: (id: any) => {
           this.activeId = id;
           this.socket.emit('activePairId', id);
         },
         debugMessage: (msg: any) => {
           this.debugMessages.push(`[${new Date().toLocaleString()}] ` + msg);
           this.socket.emit('log', msg)
         },
         setActionType: (type: string) => {
           this.currentActionType = type;
-        }
+        },
+        progressUpdate: (current: number, total: number, percentage: number) => {
+          this.socket.emit('workflowProgress', {
+            current,
+            total,
+            percentage
+          });
+        }
       },

Note: For editor mode, use socket.emit instead of socket.nsp.emit to send updates only to the connected client.

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

🤖 Prompt for AI Agents
In server/src/workflow-management/classes/Interpreter.ts (around lines 583-589
and in the interpretRecordingInEditor block at ~251-263), the debugChannel
options are missing a progressUpdate callback for editor mode; add a
progressUpdate: (current: number, total: number, percentage: number) => {
this.socket.emit('workflowProgress', { current, total, percentage }); } entry to
the debugChannel/options passed into interpretRecordingInEditor so editor-mode
sends the same progress events but only to the connected client via socket.emit
instead of socket.nsp.emit.

this.currentActionName = name;
},
progressUpdate: (current: number, total: number, percentage: number) => {
this.socket.nsp.emit('workflowProgress', {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's read the context around line 584 in Interpreter.ts
cat -n server/src/workflow-management/classes/Interpreter.ts | sed -n '570,600p'

Repository: getmaxun/maxun

Length of output: 1340


🏁 Script executed:

# Also, let's get a broader view of the progressUpdate function and its context
cat -n server/src/workflow-management/classes/Interpreter.ts | sed -n '550,610p'

Repository: getmaxun/maxun

Length of output: 2455


🏁 Script executed:

# Check if there are any comments about the socket emit patterns
rg -n "socket\.nsp\.emit|socket\.emit" server/src/workflow-management/classes/Interpreter.ts -A 3 -B 3

Repository: getmaxun/maxun

Length of output: 5187


🏁 Script executed:

# Let's also check other files in the workflow-management folder to understand the pattern
rg -n "socket\.nsp\.emit|socket\.emit" server/src/workflow-management/ -l

Repository: getmaxun/maxun

Length of output: 163


🏁 Script executed:

# Check Generator.ts for socket emit patterns
rg -n "socket\.nsp\.emit|socket\.emit" server/src/workflow-management/classes/Generator.ts -A 2 -B 2 | head -100

Repository: getmaxun/maxun

Length of output: 2287


Change socket.nsp.emit to socket.emit for progress updates.

socket.nsp.emit('workflowProgress', ...) at line 584 broadcasts the progress update to all clients in the namespace. This is inconsistent with the rest of the codebase and will cause all concurrent users to receive each other's progress updates.

All other event emissions in this file (and throughout the codebase) use this.socket.emit() to send to the individual client. The progressUpdate callback should do the same:

Fix
        progressUpdate: (current: number, total: number, percentage: number) => {
-          this.socket.nsp.emit('workflowProgress', {
+          this.socket.emit('workflowProgress', {
             current,
             total,
             percentage
           });
         },
📝 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.

Suggested change
this.socket.nsp.emit('workflowProgress', {
progressUpdate: (current: number, total: number, percentage: number) => {
this.socket.emit('workflowProgress', {
current,
total,
percentage
});
},
🤖 Prompt for AI Agents
In server/src/workflow-management/classes/Interpreter.ts around line 584, the
progress update currently uses this.socket.nsp.emit which broadcasts to the
entire namespace; change it to this.socket.emit to send the update only to the
connected client. Replace the this.socket.nsp.emit call in the progressUpdate
callback with this.socket.emit keeping the same event name ('workflowProgress')
and payload structure so it matches the rest of the file's client-specific
emissions.

@amhsirak amhsirak requested a review from RohitR311 January 2, 2026 06:16
@amhsirak amhsirak merged commit f0b37a8 into develop Jan 4, 2026
1 check passed
@amhsirak amhsirak deleted the run-progress branch April 10, 2026 11:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants