Skip to content

fix(condition): execution with subflow sentinels follow-on, snapshot highlighting, duplicate terminal logs #3429

Merged
icecrasher321 merged 8 commits intostagingfrom
fix/condition-else-exec
Mar 6, 2026
Merged

fix(condition): execution with subflow sentinels follow-on, snapshot highlighting, duplicate terminal logs #3429
icecrasher321 merged 8 commits intostagingfrom
fix/condition-else-exec

Conversation

@icecrasher321
Copy link
Collaborator

Summary

  • Consecutive condition blocks with one error edge followed by success edge leads to duplicated error in terminal error log
  • Execution failure when else edge not connected and condition block is followed by subflow. This is due to lack of differentiatation between dead end ports and actually unhit ports.
  • Snapshot highlighting heuristic function replaced with actual marking of edges during execution.

Type of Change

  • Bug fix

Testing

Tested manually

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel
Copy link

vercel bot commented Mar 6, 2026

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Mar 6, 2026 0:57am

Request Review

@cursor
Copy link

cursor bot commented Mar 6, 2026

PR Summary

Medium Risk
Touches executor edge activation/deactivation and condition/loop/parallel outputs, which can change workflow control-flow in subtle ways; mitigated by expanded regression tests and largely additive client-side highlighting logic.

Overview
Fixes a control-flow bug where condition/router blocks selecting a branch with no outgoing edge could incorrectly queue downstream loop/parallel sentinels and execute follow-on blocks; EdgeManager now treats this as a routed dead-end and only allows cascade sentinel activation when it is the enclosing sentinel for the same subflow.

Updates the condition handler to always set selectedOption (and persist the decision) even when the chosen branch has no connection, and filters error/_pauseMetadata out of propagated source outputs to avoid false error/pausing behavior.

Reworks execution-path highlighting: the client now marks outgoing edges on block completion/error based on block output (mirroring executor edge activation rules), and the preview graph uses captured block output to highlight condition/router branches accurately.

Ensures empty loop/parallel containers still appear as completed by emitting synthetic start/complete events + logs (emitEmptySubflowEvents) when subflows are skipped, and updates trace span building so container spans without iterations are retained.

Written by Cursor Bugbot for commit 254cc4a. Configure here.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 6, 2026

Greptile Summary

This PR fixes three related bugs in condition block execution involving subflow sentinels, duplicate terminal error logs, and inaccurate snapshot edge highlighting. The changes span the executor core, orchestrators, client-side execution hooks, and the preview workflow component.

Key changes:

  • Condition handler (condition-handler.ts): filterSourceOutput now strips the error field (in addition to _pauseMetadata) from upstream block output before spreading it into the condition block's result, eliminating duplicate error entries in consecutive condition chains. Additionally, when a condition evaluates to true but has no outgoing edge, selectedCondition (not null) is now returned so selectedOption is always set in the output.
  • Edge manager (edge-manager.ts): The cascade-target queuing guard now checks isUnroutedDeadEnd — requiring both zero activated targets AND absence of selectedOption/selectedRoute. This prevents subflow sentinels from being spuriously queued when a condition deliberately routes to a path that has no wired edge.
  • Snapshot highlighting (use-workflow-execution.ts, workflow-execution-utils.ts, preview-workflow.tsx): Edge status is now marked at block completion (outgoing edges based on actual output) instead of block start (incoming edges via heuristic), making highlight state accurate for condition, router, and error paths.
  • Empty subflow events (loop.ts, parallel.ts): Orchestrators now emit onBlockStart/onBlockComplete SSE events for loops/parallels that execute zero iterations, so they appear correctly in the terminal and trace spans.
  • Trace spans (trace-spans.ts): Container spans for empty subflows (no iteration spans) are no longer incorrectly filtered out as duplicate container entries.

Confidence Score: 4/5

  • Safe to merge — the fixes are well-targeted and backed by new tests; minor style concerns don't affect correctness.
  • All three described bugs have clear, targeted fixes with corresponding new test cases. The logic changes in edge-manager.ts and condition-handler.ts are well-reasoned and the new isUnroutedDeadEnd guard correctly differentiates between a true structural dead-end and a deliberate no-edge routing decision. The main concerns are non-blocking: activatedEdges is computed but unused, emitEmptySubflowEvents is duplicated between orchestrators, and the client-side shouldActivateEdgeClient is missing the loop/parallel routing guards present on the server side (low practical risk since sentinel edges are likely not in visible React Flow edges, but it creates a divergence).
  • apps/sim/executor/orchestrators/loop.ts and apps/sim/executor/orchestrators/parallel.ts (duplicated emitEmptySubflowEvents); apps/sim/app/workspace/[workspaceId]/w/[workflowId]/utils/workflow-execution-utils.ts (missing loop/parallel routing guards in shouldActivateEdgeClient)

Important Files Changed

Filename Overview
apps/sim/executor/execution/edge-manager.ts Return type changed from string[] to ProcessOutgoingEdgesResult; adds activatedEdges tracking alongside readyNodes; fixes dead-end detection by checking isUnroutedDeadEnd (requires both no activated targets AND no selectedOption/selectedRoute), preventing subflow sentinels from spuriously firing when a condition deliberately routes to a no-edge path.
apps/sim/executor/handlers/condition/condition-handler.ts Two key fixes: (1) filterSourceOutput now strips both _pauseMetadata AND error from source output, preventing upstream error fields from propagating into the condition block's own result (fixing duplicate error logs). (2) When a condition matches but its edge has no connection, selectedCondition is now returned (not null) so selectedOption is set in the output, enabling the EdgeManager to detect the routing decision correctly.
apps/sim/app/workspace/[workspaceId]/w/[workflowId]/utils/workflow-execution-utils.ts New markOutgoingEdgesFromOutput and shouldActivateEdgeClient functions mirror server-side edge activation logic on the client, replacing the previous heuristic of marking incoming edges on block start with accurate outgoing edge marking on block complete. Missing guard clauses for loop_exit/loop_continue/parallel_exit handles compared to server-side EdgeManager.
apps/sim/executor/orchestrators/loop.ts Adds emitEmptySubflowEvents calls for forEach (empty collection), for (0 iterations), while (no condition), and while (false initial condition), so empty loops emit proper start/complete SSE events. The helper method is a verbatim duplicate of the one added to parallel.ts.
apps/sim/executor/orchestrators/parallel.ts Adds emitEmptySubflowEvents call for empty distribution (0 branches). The helper implementation is identical to the one in loop.ts, representing code duplication.
apps/sim/lib/logs/execution/trace-spans/trace-spans.ts Fixes duplicate container spans by building a containerIdsWithIterations set and excluding non-error container spans only when their blockId is in that set, allowing empty containers (no iterations) to pass through as non-container spans.

Sequence Diagram

sequenceDiagram
    participant C as ConditionHandler
    participant EM as EdgeManager
    participant Eng as ExecutionEngine
    participant Client as Client Hook

    Note over C: Condition evaluates true, no outgoing edge
    C->>C: filterSourceOutput (strips error + _pauseMetadata)
    C->>C: evaluateConditions() → selectedCondition set, selectedConnection null
    C-->>Eng: return { selectedOption: conditionId, conditionResult: true, selectedPath: null }

    Note over EM: processOutgoingEdges called
    EM->>EM: shouldActivateEdge per outgoing edge
    EM->>EM: isUnroutedDeadEnd = (activatedTargets==0) AND !selectedOption AND !selectedRoute
    Note over EM: selectedOption IS set → isUnroutedDeadEnd = false
    EM->>EM: cascadeTargets NOT queued (sentinel suppressed)
    EM-->>Eng: { readyNodes: [], activatedEdges: [] }

    Note over Client: Block completes
    Client->>Client: markOutgoingEdges(blockId, output)
    Client->>Client: shouldActivateEdgeClient per outgoing UI edge
    Client-->>Client: setEdgeRunStatus for matching edges only
Loading

Last reviewed commit: 270cba4

@icecrasher321
Copy link
Collaborator Author

bugbot run

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

@icecrasher321 icecrasher321 merged commit d640fa0 into staging Mar 6, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant